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()

 

 

 

posted @ 2021-02-02 16:36  俄罗斯方块  阅读(1195)  评论(0编辑  收藏  举报