前言
在处理异步任务时,我们经常需要同时发起多个请求。Promise 提供的静态方法能让我们优雅地控制多个并发异步任务。本文将深度对比 all、race、any 和 allSettled 的区别与应用场景。
一、 方法详解与对比
1. Promise.all() —— “全员通过制”
-
概念:将多个 Promise 实例包装成一个。
-
状态决定:
- Fulfilled:所有实例都成功。
- Rejected:只要有一个失败,整体立即失败。
-
应用场景:多个接口联动,必须全部拿到数据才能渲染页面。
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);
Promise.all([p1, p2, p3])
.then((results) => {
console.log(results); // [1, 2, 3] 顺序与传入一致
})
.catch((err) => {
console.error('其中一个失败了', err);
});
2. Promise.race() —— “竞速制”
- 概念:谁跑得快就听谁的。
- 状态决定:状态取决于第一个改变状态的实例。
- 应用场景:请求超时控制。
const p1 = new Promise((resolve) =>
setTimeout(() => resolve('1秒后成功'), 1000)
);
const p2 = new Promise((resolve) =>
setTimeout(() => resolve('500毫秒后成功'), 500)
);
Promise.race([p1, p2]).then(
(res) => console.log(`测试1结果:${res} (竞速赢家)`, 'success'),
(err) => console.log(`测试1失败:${err}`, 'error')
);
3. Promise.any() —— “择优录取制”
-
概念:只要有一个成功就算成功。
-
状态决定:
- Fulfilled:只要有一个成功。
- Rejected:全部都失败时才失败(返回
AggregateError)。
-
应用场景:从多个备用服务器获取相同资源。
const p1 = Promise.resolve(1);
const p2 = Promise.reject(2);
const p3 = Promise.reject(3);
Promise.any([p1, p2, p3])
.then((res) => console.log(`有一个成功了:${res}`, 'success'))
.catch((err) => console.log(`所有都失败了:${err}`, 'error'));
4. Promise.allSettled() —— “结果导向制”
- 概念:无论成功失败,我全都要。
- 状态决定:永远是 fulfilled(在所有实例都结束后)。
- 应用场景:执行多个互不影响的操作,最后统一统计结果。
二、 核心差异对比表
为了 scannability(易读性),我们通过表格直观对比:
| 方法 | 成功条件 (Fulfilled) | 失败条件 (Rejected) | 结果返回值 |
|---|---|---|---|
.all() | 全部成功 | 任意一个失败 | 成功结果数组(按序) |
.race() | 任意一个最先成功 | 任意一个最先失败 | 第一个改变状态的值 |
.any() | 任意一个成功 | 全部失败 | 第一个成功的值 |
.allSettled() | 所有任务结束 | 从不(状态永远成功) | 包含状态和值的对象数组 |
三、 总结
1. Promise.all 与 Promise.race 的直观区别
- Promise.all:照顾“跑得最慢”的。必须等最慢的一个完成,且全员合格,才给最终结果。
- Promise.race:关注“跑得最快”的。最快的那个一旦过线,无论输赢,比赛立刻结束。
2. 进阶使用
在项目中,配合 async/await 使用更加优雅:
const fetchData = async () => {
try {
// 强类型约束结果数组
const [user, orders] = await Promise.all<[UserType, OrderType[]]>([
getUserInfo(),
getOrderList()
]);
console.log(user, orders);
} catch (error) {
// 处理第一个捕获到的错误
}
};