JS-一文带你彻底搞懂 Promise 并发控制:all, race, any, allSettled

158 阅读2分钟

前言

在处理异步任务时,我们经常需要同时发起多个请求。Promise 提供的静态方法能让我们优雅地控制多个并发异步任务。本文将深度对比 allraceanyallSettled 的区别与应用场景。

一、 方法详解与对比

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) {
    // 处理第一个捕获到的错误
  }
};