如何优雅检测所有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状态:
- 用
Promise.allSettled等待所有Promise完成,它会返回一个包含每个Promise结果的数组,每个结果对象都有status字段(fulfilled表示resolve,rejected表示reject)。 - 遍历结果数组,判断是否所有结果的
status都是rejected。 - 根据判断结果,要么执行"全部拒绝"的逻辑,要么执行"至少一个成功"的逻辑。
实现代码
方案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




