You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何优雅检测所有Promise均被拒绝并执行指定函数?

解决方案:自定义工具函数实现"所有Promise均被拒绝"的检测

你说得对,Promise.all确实没法直接满足你的需求——它要么在至少一个Promise resolve时进入.then,要么在至少一个Promise reject时进入.catch,没法区分"部分reject"和"全部reject"的情况。我们可以通过封装一个类似Promise.all的工具函数来解决这个问题,完全不需要修改原有Promise的resolve/reject逻辑。

核心思路

利用Promise.allSettled(ES2020引入,现代浏览器和Node.js都支持)获取所有Promise的最终状态,然后检查是否所有Promise都处于rejected状态:

  1. Promise.allSettled等待所有Promise完成,它会返回一个包含每个Promise结果的数组,每个结果对象都有status字段(fulfilled表示resolve,rejected表示reject)。
  2. 遍历结果数组,判断是否所有结果的status都是rejected
  3. 根据判断结果,要么执行"全部拒绝"的逻辑,要么执行"至少一个成功"的逻辑。

实现代码

方案1:返回Promise,保持链式调用风格

这个方案和Promise.all的用法几乎一致,你可以继续用.then.catch来处理两种情况:

function allRejected(promises) {
  return Promise.allSettled(promises).then(results => {
    // 检查所有Promise是否都被拒绝
    const isAllRejected = results.every(result => result.status === 'rejected');
    
    if (isAllRejected) {
      // 全部拒绝时,reject并传递所有拒绝原因
      return Promise.reject(results.map(r => r.reason));
    } else {
      // 至少一个成功时,resolve结果数组
      return Promise.resolve(results);
    }
  });
}

测试你的示例

把你的测试代码用这个工具函数替换Promise.all,就能得到你期望的输出:

// some promises to play with
let promise1 = new Promise((resolve, reject) => {setTimeout(resolve, 100)});
let promise2 = new Promise((resolve, reject) => {setTimeout(resolve, 100)});
let promise3 = new Promise((resolve, reject) => {setTimeout(resolve, 100)});
let promise4 = new Promise((resolve, reject) => {setTimeout(reject, 100)});
let promise5 = new Promise((resolve, reject) => {setTimeout(reject, 100)});

// 测试1:全部resolve
allRejected([promise1, promise2, promise3])
  .then(() => console.log('1: all promises have been resolved'))
  .catch(() => console.log('1: one or more promises have been rejected'));
// 输出:1: all promises have been resolved

// 测试2:部分reject
allRejected([promise1, promise2, promise4])
  .then(() => console.log('2: one or more promises have been resolved'))
  .catch(() => console.log('2: one or more promises have been rejected'));
// 输出:2: one or more promises have been resolved

// 测试3:部分resolve,部分reject
allRejected([promise2, promise4])
  .then(() => console.log('3: one or more promises have been resolved'))
  .catch(() => console.log('3: all promises have been rejected'));
// 输出:3: one or more promises have been resolved

// 测试4:全部reject
allRejected([promise5, promise4])
  .then(() => console.log('4: one or more promises have been resolved'))
  .catch(() => console.log('4: all promises have been rejected'));
// 输出:4: all promises have been rejected

方案2:直接传入回调函数(更贴合你的需求描述)

如果你希望更直观地指定"全部拒绝时执行的函数",可以封装成下面的形式:

function whenAllRejected(promises, onAllRejected, onAtLeastOneResolved) {
  return Promise.allSettled(promises).then(results => {
    const isAllRejected = results.every(result => result.status === 'rejected');
    
    if (isAllRejected) {
      onAllRejected?.();
    } else {
      onAtLeastOneResolved?.();
    }
  });
}

使用方式:

whenAllRejected(
  [promise2, promise4],
  () => console.log('3: all promises have been rejected'),
  () => console.log('3: one or more promises have been resolved')
);
// 输出:3: one or more promises have been resolved

whenAllRejected(
  [promise5, promise4],
  () => console.log('4: all promises have been rejected'),
  () => console.log('4: one or more promises have been resolved')
);
// 输出:4: all promises have been rejected

兼容性说明

Promise.allSettled在ES2020中被标准化,如果你需要兼容旧环境,可以用Promise.all手动模拟allSettled的行为:

// 模拟Promise.allSettled的polyfill
function allSettled(promises) {
  return Promise.all(promises.map(p => 
    p.then(
      value => ({ status: 'fulfilled', value }),
      reason => ({ status: 'rejected', reason })
    )
  ));
}

把上面的polyfill放在代码最前面,就可以在不支持allSettled的环境中使用我们的工具函数了。

内容的提问来源于stack exchange,提问作者Rob Monhemius

火山引擎 最新活动