For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-promise-allSettled.md.
  • 简体中文
  • Promise.allSettled S4

    提案概览
    提案速览

    该提案引入了 Promise.allSettled,一种组合器,它会等待所有输入 promise 敲定(兑现或拒绝)并返回其状态快照数组,不会短路。它解决了需要处理多个异步操作、且无论个别失败如何都需要完整结果的需求。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    Promise.allSettled

    ECMAScript 提案及 Promise.allSettled 的参考实现。

    作者: Jason Williams (BBC), Robert Pamely (Bloomberg), Mathias Bynens (Google)

    推进者: Mathias Bynens (Google)

    阶段: 4

    概述与动机

    在 Promise 领域有四种主要的组合器

    名称描述
    Promise.allSettled不会短路本提案 🆕
    Promise.all当输入值被拒绝时短路ES2015 加入 ✅
    Promise.race当输入值被敲定(settled)时短路ES2015 加入 ✅
    Promise.any当输入值被兑现(fulfilled)时短路单独提案 🔜

    这些组合器在用户态 promise 库中普遍可用,且各自独立有用,分别服务于不同的用例。

    _本_组合器的一个常见用例是:在多个请求完成后采取行动,无论它们是成功还是失败。 其他 Promise 组合器可能会短路,丢弃在竞争中未能达到某种状态的输入值的结果。 Promise.allSettled 的独特之处在于它始终等待所有输入值。

    Promise.allSettled 返回一个 promise,该 promise 在所有原始 promise 都敲定(即变为 fulfilled 或 rejected)之后,以一个 promise 状态快照数组兑现。

    为什么用 allSettled

    我们说一个 promise 是 settled(已敲定)的,如果它不是 pending(待定)的,即它要么是 fulfilled(已兑现)要么是 rejected(已拒绝)。关于相关术语的更多背景,请参阅 promise 状态与命运

    此外,allSettled 这个名称在实现此功能的用户态库中很常用。见下文。

    示例

    目前,你需要遍历 promise 数组,并通过已知状态(要么通过 resolved 分支,要么通过 rejected 分支)返回一个新值。

    function reflect(promise) {
      return promise.then(
        (v) => {
          return { status: 'fulfilled', value: v };
        },
        (error) => {
          return { status: 'rejected', reason: error };
        }
      );
    }
    
    const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
    const results = await Promise.all(promises.map(reflect));
    const successfulPromises = results.filter(p => p.status === 'fulfilled');

    所提议的 API 允许开发者处理这些情况,而无需创建 reflect 函数,也无需将中间结果赋值给临时对象来映射:

    const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
    const results = await Promise.allSettled(promises);
    const successfulPromises = results.filter(p => p.status === 'fulfilled');

    收集错误的示例:

    这里我们只对失败的 promise 感兴趣,因此收集原因。allSettled 让我们可以很容易做到这一点。

    const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
    
    const results = await Promise.allSettled(promises);
    const errors = results
      .filter(p => p.status === 'rejected')
      .map(p => p.reason);

    真实世界场景

    一个常见操作是知道所有请求何时完成,而不管每个请求的状态如何。这允许开发者以渐进增强的思路进行构建。并非所有 API 响应都是必需的。

    没有 Promise.allSettled,这会比应有的更棘手:

    const urls = [ /* ... */ ];
    const requests = urls.map(x => fetch(x)); // 想象其中一些会失败,一些会成功。
    
    // 在第一次拒绝时短路,所有其他响应都丢失
    try {
      await Promise.all(requests);
      console.log('所有请求已完成;现在我可以移除加载指示器。');
    } catch {
      console.log('至少一个请求失败,但一些请求可能仍未完成!糟糕。');
    }

    使用 Promise.allSettled 更适合我们想要执行的操作:

    // 我们知道所有 API 调用都已完成。我们使用 finally,但 allSettled 永远不会拒绝。
    Promise.allSettled(requests).finally(() => {
      console.log('所有请求都已完成:要么失败要么成功,我不在乎');
      removeLoadingIndicator();
    });

    用户态实现

    其他语言中的命名

    类似的功能在其他语言中存在,但名称不同。由于跨语言没有统一的命名机制,本提案遵循上面显示的用户态 JavaScript 库的命名先例。以下示例由 jasonwilliams 和 benjamingr 提供。

    Rust

    futures::join(类似于 Promise.allSettled)。"同时轮询多个 future,完成后返回所有结果的元组。"

    futures::try_join(类似于 Promise.all

    C#

    Task.WhenAll(类似于 ECMAScript Promise.all)。你可以使用 try/catch 或 TaskContinuationOptions.OnlyOnFaulted 来实现与 allSettled 相同的行为。

    Task.WhenAny(类似于 ECMAScript Promise.race

    Python

    asyncio.wait 使用 ALL_COMPLETED 选项(类似于 Promise.allSettled)。返回任务对象,类似于 allSettled 的检查结果。

    Java

    allOf(类似于 Promise.all

    Dart

    Future.wait(类似于 ECMAScript Promise.all

    进一步阅读

    TC39 会议记录

    规范

    实现