Promise.all、Promise.race 和 Promise.allSettled 的区别
Promise.all、Promise.race 和 Promise.allSettled 都是 JavaScript 中用于处理多个 Promise 并发的方法。它们都接收一个 Promise 可迭代对象(通常是数组),但它们在何时改变状态(resolve/reject)以及返回的结果上有显著的区别。
为了方便理解,我们可以用通俗的比喻来解释,并结合代码和表格进行对比。
1. Promise.all() —— “一票否决”
比喻:团队合作完成一个项目,必须所有人都完成任务,项目才算成功;只要有一个人搞砸了,整个项目直接宣告失败。
- 执行机制:等待数组中所有的 Promise 都变为
fulfilled(成功)状态,它才会成功。 - 成功返回值:返回一个数组,包含所有 Promise 的成功结果,且顺序与传入时的顺序完全一致。
- 失败机制:只要数组中有一个 Promise 变为
rejected(失败),Promise.all就会立即失败(短路效应),并返回第一个失败的 Promise 的错误信息。其他还在执行的 Promise 依然会继续执行,但它们的结果会被忽略。 - 适用场景:多个并发请求彼此依赖,必须全部成功才能进行下一步(例如:获取用户信息和用户权限都成功后,才渲染页面)。
代码示例:
javascript
const p1 = Promise.resolve('成功 1');
const p2 = Promise.resolve('成功 2');
const p3 = Promise.reject('失败 3');
// 全部成功的情况
Promise.all([p1, p2]).then(res => console.log(res));
// 输出: ['成功 1', '成功 2']
// 包含失败的情况
Promise.all([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err));
// 输出: '失败 3' (因为 p3 失败了,直接走 catch)
2. Promise.race() —— “百米赛跑”
比喻:几个人赛跑,谁第一个冲过终点(无论他是正常跑完还是中途摔倒出局),比赛就结束,结果以这个第一名为准。
- 执行机制:谁执行得最快,就返回谁的结果。
- 成功/失败机制:只要数组中第一个 Promise 的状态发生改变(无论是
fulfilled还是rejected),Promise.race的状态就会跟着改变,并返回那个最快的 Promise 的结果/错误。 - 适用场景:网络请求超时处理。将一个真实的请求和一个定时器 Promise 放在一起,如果定时器先触发,就提示超时。
代码示例:
javascript
const p1 = new Promise((resolve) => setTimeout(() => resolve('请求数据成功'), 500));
const p2 = new Promise((_, reject) => setTimeout(() => reject('请求超时'), 300)); // 这个更快
Promise.race([p1, p2])
.then(res => console.log(res))
.catch(err => console.log(err));
// 输出: '请求超时' (因为 300ms < 500ms,p2 先执行完)
3. Promise.allSettled() —— “无论结果如何,都要等到最后”
比喻:期末考试,无论学生考及格还是不及格,老师都要等所有人都交卷后,把所有人的成绩单收上来汇总。
- (ES2020 引入)
- 执行机制:等待数组中所有的 Promise 都执行完毕(无论成功还是失败)。
- 成功返回值:永远不会触发 reject。它会等待所有 Promise 执行结束后,返回一个数组,包含每一个 Promise 的结果对象。
- 成功的对象格式:
{ status: 'fulfilled', value: 结果值 } - 失败的对象格式:
{ status: 'rejected', reason: 错误原因 }
- 成功的对象格式:
- 适用场景:多个互不依赖的请求并发执行,你希望知道每一个请求的结果,即使有些失败了,也不影响你处理其他成功的请求(例如:批量上传 3 张图片,1 张失败了,但你想知道哪两张成功了,哪一张失败了)。
代码示例:
javascript
const p1 = Promise.resolve('成功 1');
const p2 = Promise.reject('失败 2');
const p3 = Promise.resolve('成功 3');
Promise.allSettled([p1, p2, p3]).then(res => console.log(res));
/* 输出:
[
{ status: 'fulfilled', value: '成功 1' },
{ status: 'rejected', reason: '失败 2' },
{ status: 'fulfilled', value: '成功 3' }
]
*/
💡 核心总结对照表
| 方法 | 核心逻辑 | 何时进入 then (成功) |
何时进入 catch (失败) |
返回值格式 |
|---|---|---|---|---|
Promise.all |
全部成功才算赢 | 所有 Promise 都成功 | 只要有 1个 Promise 失败就立即报错 | [结果1, 结果2...] |
Promise.race |
最快的说了算 | 第一个完成且是成功 | 第一个完成且是失败 | 最先完成的那个值或错误 |
Promise.allSettled |
包容一切等结局 | 所有 Promise 都执行完毕(无论成败) | 永远不报错(除非传入的不是可迭代对象) | [{status, value/reason}, ...] |
(补充扩展:ES2021 还引入了 Promise.any(),它的逻辑是“只要有一个成功就算成功”,只有全部失败才会报错,正好和 Promise.all 是相反的。)