(1)“promise”是一个对象或者函数,该对象或者函数有一个then方法;
(2)“thenable”是一个对象或者函数,用来定义then方法;
(3)“value”是promise状态成功时的值;
(4)“reason”是promise状态失败时的。
promise状态
异步操作有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败)
状态改变,只可以pending-->fulfilled, pending-->rejected变化;只有处于fulfilled,rejected,状态就不会变化了,即resolved(已定型)
状态的缺点
无法取消promise,一旦新建,就会立即执行,无法中途取消。
如果不设置回调函数,promise内部抛出的错误,不会反映到外部。
当处于pending 时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
避免了层层嵌套的回调函数,解决回调地狱
基本用法:
1.promise是一个构造函数,用来生成promise的实例
接受一个函数作为参数,
const promise = new Promise(function(resolve,reject){
//...some code
if(异步操作成功){
resolve(value);
}else{
reject(error);
}
})
Promise 构造函数接受一个函数作参数,两个参数:resolve和reject,它们由js引擎提供,不用自己部署。
resolve函数作用,将promise对象的状态从“未完成”变成“成功”(pending-->resolved),在异步操作对象成功时调用,并将异步操作的结果作为参数传出去。
reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise实例生成以后,用then方法分别指定resolved和rejected的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法
then方法接受两个函数作为参数,第一个是promise 执行成功的回调,第二个是失败时的回调,两个函数只会有一个被调用。
Promise新建后会立即执行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise
。然后,then
方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved
最后输出。
异步加载图片
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
上面代码中,使用Promise
包装了一个图片加载的异步操作。如果加载成功,就调用resolve
方法,否则就调用reject
方法。
用Promise
对象实现的 Ajax 操作的例子。
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
上面代码中,getJSON
是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise
对象。需要注意的是,在getJSON
内部,resolve
函数和reject
函数调用时,都带有参数。
如果调用resolve
函数和reject
函数时带有参数,那么它们的参数会被传递给回调函数。reject
函数的参数通常是Error
对象的实例,表示抛出的错误;resolve
函数的参数除了正常的值以外,还可能是另一个 Promise 实例,比如像下面这样。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
上面代码中,p1
和p2
都是 Promise 的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。
then 方法的特点
在 JavaScript 事件队列的当前运行完成之前,回调函数永远不会被调用。
const p = new Promise(function(resolve,reject){
resolve('success');
});
//回调函数就是被作为参数传递的函数
p.then(function(value){
console.log(value);
});
console.log('first');
// first
// success
promise.prototype.then()
then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
通过.then形式添加的回调函数,不论什么时候,都会被调用。通过多次调用.then,可以添加多个回调函数,他们会按照插入顺序并且独立运行。
const p = new Promise(function(resolve,reject){
resolve(1);
}).then(function(value){ // 第一个then // 1
console.log(value);
return value * 2;
}).then(function(value){ // 第二个then // 2
console.log(value);
}).then(function(value){ // 第三个then // undefined
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ // 第四个then // resolve
console.log(value);
return Promise.reject('reject');
}).then(function(value){ // 第五个then //reject:reject
console.log('resolve:' + value);
}, function(err) {
console.log('reject:' + err);
});
最后一个为什么执行reject:reject?
上一个return的是一个 promise 状态是reject
所以在下一个then里面 走的是 reject的
then 方法将返回一个resolved或rejected状态的promise对象对于链式调用,且promise对象的值就是这个返回值。
Promise.prototype.catch()
用于指定发生错误时的回调函数。一般来说不要在then方法中定义rejected状态的回调函数,而应多使用catch方法。
promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获位置。错误总会被下一个catch语句捕获。
catch返回的还是一个Promise对象。
Promise.all()
用于将多个promise实例包装成一个新的Promise实例。状态由参数(都是Promise对象的实例)决定,都为resolved才是resolved,or 是rejected.
如果参数自身定义了catch(), 那么rejected的时候不会触发Promise.all()的catch()方法
const p1 = new Promise((resolve,reject)=>{
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve,reject) => {
throw new Error('报错了');
})
.then(result => console.log(result))
.catch(e => console.log(e))
Promise.all([p1,p2])
.then(result => console.log(result)) // [hello,undefined]
.catch(e => console.log(e));
//如果作为参数的Promise实例自身定义了catch方法,那么它被rejected的时候不会触发Promise.all()的catch方法
const p1 = new Promise((resolve,reject)=>{
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve,reject) => {
throw new Error('报错了');
})
.then(result => console.log(result))
Promise.all([p1,p2])
.then(result => console.log(result))
.catch(e => console.log(e)); //Error: 报错了
注意点:
注意要返回或终止Promise链
大多数浏览器不能终止promise链中的rejection,建议后面跟上.catch(error => console.log(error));
来源:《阮一峰ES6》,菜鸟教程