如何让Promise.all拒绝(reject)后依然有效?

930 阅读2分钟

前言

在处理多个并发请求的时候,大部分时候我们会选择用Promise.all
首先,我们先来看看如何实现一个Promise.all

Promise.all = function (promises) {
 return new Promise((resolve, reject) => {
     if (Array.isArray(promises)) {
         let result = []; // 存储结果
         let count = 0; // 计数器
         // 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
         if (promises.length === 0) {
             return resolve(promises);
         }
         promises.forEach((item, index) => {
             //  判断参数是否为promise与thenable对象
             if (item instanceof Promise || (item instanceof Object && 'then' in item)) {
                 item.then(
                     value => {
                         count++;
                         result[index] = value;
                         count === promises.length && resolve(result);
                     },
                     reason => {
                         reject(reason);
                     }
                 )
             } else {
                 count++;
                 result[index] = item;
                 count === promises.length && resolve(result);
             }
         })
     } else {
         return reject(new TypeError('Argument is not iterable'))
     }
 })
}

从实现的code可以看出:

  • 该方法指当所有在可迭代参数中的 promise 已完成,或者第一个传递的 promise(指 reject)失败时,返回 promise。
  • 当其中任何一个被拒绝的话。Promise.all([..])就会立即被拒绝,并丢弃来自其他所有promise的全部结果。
  • promise.all 中任何一个 promise 出现错误的时候都会执行reject,导致其它正常返回的数据也无法使用。

如何让Promise.all在抛出异常后依然有效呢?

方法一

let p1 = new Promise((resolve, reject) => {
  resolve("p1");
});
let p2 = new Promise((resolve, reject) => {
  resolve("p2");
});
let p3 = new Promise((resolve, reject) => {
  reject("p3");
});
Promise.all([p1, p2, p3].map(p => p instanceof Promise? p.catch(e => e):Promise.resolve(p)))
  .then(values => {
    console.log(values);
  }).catch(err => {
    console.log(err);
  })

输出:["p1","p2","p3"]
解析

  • 一、我们知道Promiseresolve过后始终会被then中第一个参数获取到。
  • 二、并且Promise链式调用onFulfilled成功回调, onRejected失败回调后的返回值可以返回一个Promise实例或者其他类型的值(最终会被Promise.resolve包装)
  • 利用这两点,就可以让Promise.all接收的数组中全部经过catch后返回。现在所有的值都是resolve成功的状态了。最后Promise.all也能成功捕获到所有的状态。
  • 如果不了解Promise相关规范的可以移步Promise A+ 规范,这里就不多介绍了。

方法二

使用ES2020(即 ES11)Promise新增的静态方法allSettled即 Promise.allSettled 替代 Promise.all()

let p1 = new Promise((resolve, reject) => {
  resolve("p1");
});
let p2 = new Promise((resolve, reject) => {
  resolve("p2");
});
let p3 = new Promise((resolve, reject) => {
  reject("p3");
});
Promise.allSettled ([p1, p2, p3])
  .then(values => {
    console.log(values);
  }).catch(err => {
    console.log(err);
  })

输出:

[
  {
      "status": "fulfilled",
      "value": "p1"
  },
  {
      "status": "fulfilled",
      "value": "p2"
  },
  {
      "status": "rejected",
      "reason": "p3"
  }
]

Promise.allSettled()方法返回一个promise,该promise在所有给定的promise已被解析或被拒绝后解析,并且每个对象都描述每个promise的结果。

最后

本文就讲到这里,各位看官如果文中有错希望不惜吝啬欢迎指出!