← 返回题目列表

Promise.all、race、allSettled、any 有什么区别?

高频 中等 第 15 / 28 题 更新于 2026/07/28
ES6Promise异步

简化版

Promise.all 等全部成功或任一失败;race 谁先 settled 就跟谁;allSettled 等全部结束并保留每个结果;any 等任一成功,全部失败才失败。

详细版

四个组合方法适合不同并发场景:

  • Promise.all:多个请求都成功后再继续,适合强依赖并发。
  • Promise.race:取最快完成的结果,常用于超时控制。
  • Promise.allSettled:不因单个失败中断,适合批量任务汇总。
  • Promise.any:只要一个成功就算成功,适合多源容灾。

关键差别在两个维度:等待到什么时候、失败如何传播。 面试时最好结合“3 个请求,1 个失败”的例子说明。

完整版教学

一、组合方法解决的是并发编排问题

单个 Promise 描述一个异步结果。 实际业务经常同时发起多个请求,例如用户信息、权限、配置、推荐列表。 组合方法的价值是把多个异步任务合成一个更高层的异步结果。

const p1 = fetchUser();
const p2 = fetchConfig();
const p3 = fetchPermissions();

const result = await Promise.all([p1, p2, p3]);

如果 3 个请求串行执行,每个 200ms,总耗时约 600ms。 如果并发执行并用 all 汇总,总耗时接近最慢的 200ms。 所以组合方法既是语义工具,也是性能工具。

二、Promise.all:全部成功才成功

Promise.all 接收一组 Promise。 只有所有成员都 fulfilled,整体才 fulfilled,结果数组顺序与输入顺序一致。 只要任意一个 rejected,整体立即 rejected。

const result = await Promise.all([
  Promise.resolve('A'),
  Promise.resolve('B')
]);

console.log(result); // ['A', 'B']

注意结果顺序不是完成顺序。 即使第二个请求先返回,结果仍放在索引 1。 这让调用方可以稳定地按输入位置解构结果。

p1 成功
p2 成功
p3 成功
  -> all 成功:[v1, v2, v3]

p2 失败
  -> all 失败:reason2

三、Promise.race:谁先结束就采用谁

race 的关键不是“谁先成功”,而是谁先 settled。 settled 包括 fulfilled 和 rejected。 因此最快失败的 Promise 也会让整体失败。

function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error('timeout')), ms);
  });
}

await Promise.race([fetchData(), timeout(3000)]);

这个例子用 3000ms 的定时器和真实请求比赛。 如果请求 1200ms 成功,整体成功。 如果请求 5000ms 才返回,超时 Promise 先 rejected,整体失败。

四、Promise.allSettled:关心每个任务的最终状态

allSettled 不会因为某一个任务失败就提前中断。 它会等所有任务都 settled,然后返回每个任务的状态和值或原因。 适合批量上传、批量通知、批量预加载等场景。

const results = await Promise.allSettled([
  Promise.resolve(1),
  Promise.reject(new Error('fail')),
]);

console.log(results);
// [
//   { status: 'fulfilled', value: 1 },
//   { status: 'rejected', reason: Error('fail') }
// ]
方法成功条件失败条件是否保留全部结果
all全部成功任一失败失败时不保留
race最先成功最先失败只看第一个 settled
allSettled全部结束不会整体失败保留
any任一成功全部失败成功时只取一个

五、Promise.any:多源容灾时很好用

any 等待第一个 fulfilled。 如果某个请求失败,它不会立刻让整体失败,而是继续等其他 Promise 成功。 只有全部 rejected,整体才 rejected,并抛出 AggregateError

const fastest = await Promise.any([
  fetchFromCDN1(),
  fetchFromCDN2(),
  fetchFromOrigin()
]);

假设 3 个源分别在 100ms 失败、180ms 成功、260ms 成功。 any 会在 180ms 成功返回第二个源的结果。 同样场景用 race,100ms 的失败会直接让整体失败。

六、失败传播和取消要分开理解

Promise.allrace 的整体状态确定后,并不代表其他异步任务被自动取消。 已经发出去的网络请求通常仍会继续执行。 如果需要真正取消,要结合 AbortController 或库提供的取消机制。

const controller = new AbortController();

fetch('/api/data', { signal: controller.signal });
controller.abort();

记忆钩子:组合方法决定“等谁、信谁、怎么报结果”,不自动负责取消已经启动的副作用。

工程里如果 all 失败后不再需要其他请求结果,可以考虑主动取消。 否则可能浪费带宽,也可能产生意外的后续副作用。

七、常见误区与追问

  • 误区:race 是谁先成功就返回谁。 race 看的是谁先 settled,最快失败也会让整体失败。
  • 误区:all 失败会自动取消其他 Promise。 Promise 状态汇总不会自动取消底层任务,需要额外取消机制。
  • 误区:allSettled 只适合失败场景。 它适合需要完整任务报告的场景,成功和失败都要统计。
  • 追问:all 的结果顺序按完成顺序还是输入顺序? 按输入顺序,便于稳定解构和映射。
  • 追问:any 全部失败时抛什么?AggregateError,里面包含多个失败原因。
  • 追问:请求超时用 race 有什么坑? race 只让外层 Promise 先结束,不会自动终止真实请求。

八、加强记忆

四个方法按口令记:all 要全成,race 抢第一个结束,allSettled 收全场报告,any 抢第一个成功。答题时再补两个关键细节:结果顺序按输入顺序,组合状态不等于底层任务自动取消。