ES6 - Promise对象②:finally()、all()、race()、allSettled()、any()
1.Promise.prototype.finally()
finally()方法用于不管Promise对象最后的状态如何,都会执行的操作。
promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
finally方法的回调函数不接受任何参数,这意味着没有办法知道,钱买你的Promise状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,与状态无关的,不依赖于Promise的执行结果。
finally本质上是then方法的特例。
promise .finally(() => { // 语句 }); // 等同于 promise .then( result => { // 语句 return result; }, error => { // 语句 throw error; } );
上面代码中,如果不使用finally方法,同样的语句需要成功和失败各写一次。finally方法只需要写一次
finally的实现:
1 Promise.prototype.finally = function (callback) { 2 let p = this.constructor 3 return this.then( 4 value => p.resolve(callback()).then(() => value), 5 reason => p.resolve(callback()).then(() => { throw reason }) 6 ) 7 }
上面代码中,不管前面的Promise是fulfilled还是rejected,都会执行回调函数callback
从上面的实现还可以看到,finally
方法总是会返回原来的值。
1 // resolve 的值是 undefined 2 Promise.resolve(2).then(() => {}, () => {}) 3 4 // resolve 的值是 2 5 Promise.resolve(2).finally(() => {}) 6 7 // reject 的值是 undefined 8 Promise.reject(3).then(() => {}, () => {}) 9 10 // reject 的值是 3 11 Promise.reject(3).finally(() => {})
2.Promise.all()
此方法用于将多个Promise实例,包装成一个新的Promise实例。
只有所有的异步操作的状态都变成fulfilled或者其中一个变成了rejected,才会调用Promise.all方法后面的回调函数。
1 const p1 = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve("p1执行结束") 4 }, 1000) 5 }) 6 7 const p2 = new Promise((resolve, reject) => { 8 setTimeout(() => { 9 resolve("p2执行结束") 10 }, 3000) 11 }) 12 13 const p3 = new Promise((resolve, reject) => { 14 setTimeout(() => { 15 resolve("p3执行结束") 16 }, 2000) 17 }) 18 19 const p = Promise.all([ 20 p1, 21 p2, 22 p3 23 ]) 24 25 p.then(res => console.log(res)) // ["p1执行结束", "p2执行结束", "p3执行结束"]
上面的代码中,有三个异步操作,只有在三个异步的状态都变成了了resolved,then()方法才会返回所有异步操作的结果。如果有一个状态变成了rejected。则错误会被catch捕获到:
1 const p1 = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve("p1执行结束") 4 }, 1000) 5 }) 6 7 const p2 = new Promise((resolve, reject) => { 8 setTimeout(() => { 9 resolve("p2执行结束") 10 }, 3000) 11 }) 12 13 const p3 = new Promise((resolve, reject) => { 14 setTimeout(() => { 15 reject("p3执行失败") 16 }, 2000) 17 }) 18 19 const p = Promise.all([ 20 p1, 21 p2, 22 p3 23 ]) 24 25 p.then(res => console.log(res)) 26 .catch(err => console.log("抛出错误:", err)) // 抛出错误: p3执行失败
注意:如果作为参数的 Promise 实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
1 const p1 = new Promise((resolve, reject) => { 2 resolve('hello'); 3 }) 4 .then(result => result) 5 .catch(e => e); 6 7 const p2 = new Promise((resolve, reject) => { 8 throw new Error('报错了'); 9 }) 10 .then(result => result) 11 .catch(e => e); 12 13 Promise.all([p1, p2]) 14 .then(result => console.log(result)) 15 .catch(e => console.log("------",e)); 16 // ["hello", Error: 报错了]
3.Promise.race()
Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例
1 const p = Promise.race([p1, p2, p3]);
上面代码种,只要p1,p2,p3种有一个实例率先改变状态,p的状态就改变。那个最先改变的Promise实例的返回值,就传递给p的回调函数。
1 const p1 = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve("p1执行结束") 4 }, 6000) 5 }) 6 7 const p2 = new Promise((resolve, reject) => { 8 setTimeout(() => { 9 resolve("p2执行结束") 10 }, 3000) 11 }) 12 13 const p3 = new Promise((resolve, reject) => { 14 setTimeout(() => { 15 resolve("p3执行结束") 16 }, 2000) 17 }) 18 19 const p = Promise.race([ 20 p1, 21 p2, 22 p3 23 ]) 24 25 p.then(res => console.log(res)) // p3执行结束
上面代码中,最先执行结束的p3的结果会传递到then中的回调函数中。
4.Promise.allSettled()
Promise.allSettled()方法接受一组Promise实例作为参数,包装成一个新的Promisee实例。等到所有这些实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
示例:
1 const p1 = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve("p1执行结束") 4 }, 6000) 5 }) 6 7 const p2 = new Promise((resolve, reject) => { 8 setTimeout(() => { 9 reject("p2执行结束") 10 }, 3000) 11 }) 12 13 const p3 = new Promise((resolve, reject) => { 14 setTimeout(() => { 15 resolve("p3执行结束") 16 }, 2000) 17 }) 18 19 const p = Promise.allSettled([ 20 p1, 21 p2, 22 p3 23 ]) 24 25 p.then(res => console.log(res)) 26 /* 27 0: {status: "fulfilled", value: "p1执行结束"} 28 1: {status: "rejected", reason: "p2执行结束"} 29 2: {status: "fulfilled", value: "p3执行结束"}
30 Promise.settled执行结束
31 */
结果:异步时间最长(6s)的异步操作p1执行结束以后,打印出了异步执行的结果(都包含了状态<status>,和结果以及rejected时的reason)。这里无论单个Promise的状态是变成fulfilled还是rejected,都会等所有的状态变为最终状态。
且:一旦结束,状态总是fulfilled。不会变成rejected。所以后面可以接then。继续执行
与Promise.all()的区别:
- Promise.all()需要等到所有的状态都变成resolved或者某一个变成rejected才会返回
- 有时候,不关心异步操作的结果,只关心这些操作有没有结束。这一点Promise.allSettled()就比较实用
5.Promise.any()
该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
- 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态,
- 如果所有的参数实例都变成rejected,包装实例就变成rejected状态
示例一:所有异步都变成rejected:
1 const p1 = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 reject("p1执行结束失败") 4 }, 6000) 5 }) 6 7 const p2 = new Promise((resolve, reject) => { 8 setTimeout(() => { 9 reject("p2执行失败") 10 }, 3000) 11 }) 12 13 const p3 = new Promise((resolve, reject) => { 14 setTimeout(() => { 15 reject("p3执行失败") 16 }, 2000) 17 }) 18 19 const p = Promise.any([ 20 p1, 21 p2, 22 p3 23 ]) 24 25 p.then(res => { 26 console.log("any执行结束", res) 27 }) 28 .then(() => { 29 console.log("Promise.settled执行结束"); 30 }) 31 .catch(err => { 32 console.log("所有异步均失败", err); 33 }) 34 35 // 6s后: 所有异步均失败 AggregateError: All promises were rejected
示例二:一个异步变成fulfilled:
1 const p1 = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve("p1执行结束") 4 }, 6000) 5 }) 6 7 const p2 = new Promise((resolve, reject) => { 8 setTimeout(() => { 9 resolve("p2执行结束") 10 }, 3000) 11 }) 12 13 const p3 = new Promise((resolve, reject) => { 14 setTimeout(() => { 15 resolve("p3执行结束") 16 }, 2000) 17 }) 18 19 const p = Promise.any([ 20 p1, 21 p2, 22 p3 23 ]) 24 25 p.then(res => { 26 console.log("any执行结束", res) 27 }) 28 .then(() => { 29 console.log("Promise.settled执行结束"); 30 }) 31 .catch(err => { 32 console.log("所有异步均失败", err); 33 }) 34 35 // 2s后: 36 // - any执行结束 p3执行结束 37 // - Promise.settled执行结束
6.Promise.resolve()
有时候需要将现有对象转为Promise对象,Promise.resolve()就是这个作用。
如:
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将jQuery生成的deferred对象,转为一个新的Promise对象。
Promise.resolve()等价于下面的写法:
Promise.resolve("foo") // 等价于 new Promise(resolve => resolve("foo"))
Promise.resolve()方法的参数有四种情况:
(1)参数是一个Promise对象
那么Promise.resolve
将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,如下面这个对象:
let thenable = { then: function(resolve,reject){ resolve(123) } }
Promise.resolve()方法会将这个对象转为Promise对象,然后立即执行thenable对象的then ()方法。
1 let thenable = { 2 then: function (resolve, reject) { 3 resolve(123) 4 } 5 } 6 7 let p1 = Promise.resolve(thenable) 8 p1.then(function (value) { 9 console.log(value); // 123 10 })
上面代码中,thenable
对象的then()
方法执行后,对象p1
的状态就变为resolved
,从而立即执行最后那个then()
方法指定的回调函数,输出42
(3)参数不是具有then()方法的对象,或者根本不是对象。
如果参数是一个原始值,或者是一个不具有then()
方法的对象,则Promise.resolve()
方法返回一个新的 Promise 对象,状态为resolved
。
1 const p = Promise.resolve('Hello'); 2 3 p.then(function (s) { 4 console.log(s) 5 }); 6 // Hello
上面代码生成一个新的 Promise 对象的实例p
。由于字符串Hello
不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved
,所以回调函数会立即执行。Promise.resolve()
方法的参数,会同时传给回调函数。
(4)没有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的Promise对象。所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve()方法。
const p = Promise.resolve(); p.then(function () { // ... });
7.Promise.reject()
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s) { console.log(s) }); // 出错了
8.Promise.try()