1、Promise含义
Promise 是异步编程的一种解决方案。它的最大好处是在异步执行的流程中,把执行代码和处理结果的代码分离开。promise相当于一个容器,里面保存着某个未来才会结束的事件的结果 —— 通常是一个异步操作。
Promise对象有两大特点:
(1)对象的状态不受外界的影响。
promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有promise中的异步操作才能决定当前是哪一种状态。
(2)一旦状态改变就不会再改变,任何时候都可以得到这个结果。Promise对象的状态变化只有两种可能,从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就不会再变化,这时成为resolved(已定性)。
注意 为了写作方便,本章以后resolved统一指fulfilled状态,不包含rejected状态。
2、基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
//创作一个Promise实例
const promise = new Promise(function(resolve, reject){
if (/* 异步成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise构造函数接受一个函数作为参数,该函数有两个参数resolve和reject。这两个参数也是两个函数,由JavaScript引擎提供。
resolve函数在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数在异步操作失败时调用,并将异步操作报错的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的。这两个函数都接受Promise对象传出的值作为参数。
promise.then(function(){
//success
}, function(){
//failure
});
Promise函数新建后会立即执行,而then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行。
let promise = new Promise(function(resolve, reject){
console.log('立即执行Promise');
resolve();
});
promise.then(function(){
console.log('执行then方法的回调');
});
console.log('Hi');
//立即执行Promise
//Hi
//执行then方法的回调
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例
const P1 = new Promise(function(resolve, reject){
setTimeout(() => reject(new Error('fail')), 3000);
});
const P2 = new Promise(function(resolve, reject){
setTimeout(() => resolve(P1), 1000)
});
P2.then(result => console.log(result))
.catch(error => console.log(error))
//Error: fail
上面代码P1是一个Promise,3秒后变为rejected。P2的状态在1秒后变为resolve,resolve返回的是P1。由于P2返回的是一个Promise,导致P2自己的状态无效了,P1的状态决定了P2的状态。
注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。
new Promise((resolve, reject) => {
resolve(1);
console.log('2');
}).then(r => {
console.log(r);
});
//2
//1
调用resolve(1)以后,后面的console.log(2)还是会执行,并首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。
new Promise((resolve, reject) => {
return resolve(1);
//后面的代码将不再执行
})
3、Promise.prototype.then()
前面已经讲过then方法的用法,这边再补充一些。then方法返回的是一个新的Promise实例(不是原来的Promise实例了),因此可以采用链式写法。
new Promise((resolve, reject) => {
}).then((r) => {
}).then((r) => {
});
4、Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
const promise = new Promise(function(resolve, reject){
throw new Error('抛错喽');
});
promise.then(function(data){
}).catch(function(error){
console.log(error);
});
//Error: 抛错喽
5、Promise.prototype.finally()
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
finally方法的回调函数不接受任何参数。
Promise.then(result => {
}).then(result => {
}).catch(error => {
}).finally(() => {});
不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。
6、Promise.all()
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
Promise.all方法会接受一个数组作为参数,p1、p2、p3都是Promise实例。如果参数不说Promise实例,就会先调用Promise.resolve方法,讲参数转化为Promise实例。
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
var one = new Promise(function(resolve, reject){
setTimeout(resolve, 500, 'one');
});
var two = new Promise(function(resolve, reject){
setTimeout(resolve, 600, 'two');
});
//同时执行one和two,并在他们都完成后执行then
Promise.all([one, two]).then(function(results){
console.log(results);
});
//["one", "two"]
7、Promise.race()
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.race([p1, p2, p3]);
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给P的回调函数。
var one = new Promise(function(resolve, reject){
setTimeout(resolve, 500, 'one');
});
var two = new Promise(function(resolve, reject){
setTimeout(resolve, 600, 'two');
});
Promise.race([one, two]).then(function(result){
console.log(result);
});
//one
8、Promise.resolve()
将现有对象转化为Promise对象
const promise = Promise.resolve('foo');
//等价于
new Promise(resolve => resolve('foo'));
注意 立即resolve的Promise对象,是在本轮“事件循环”(event loop)结束时,而不是在下一轮“事件循环”的开始时。
setTimeout(function(){
console.log('three');
}, 0);
Promise.resolve().then(function(){
console.log('two');
});
console.log('one');
//one
//two
//three
setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log(‘one’)则是立即执行,因此最先输出。
9、Promise.reject()
Promise.reject方法也会返回一个新的Promise实例,该实例的状态为rejected。
注意 Promise.reject()方法的参数,会原封不动的变为后续方法的参数,这一点与Promise.resolve方法不一致。
const thenable = {
then(resolve, reject){
reject('Error');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable);
});
//true
Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“Error”这个字符串,而是thenable对象。