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/stage/3/proposal-await-dictionary.md.
  • 简体中文
  • Await Dictionary S3

    中文标题:await 字典

    提案概览
    提案速览

    该提案解决了按属性名等待多个 Promise 的效率问题,避免顺序瀑布和基于顺序的混淆。它引入了 Promise.allKeyedPromise.allSettledKeyed 静态方法,接受一个 Promise 对象并返回一个具有相同键的对象,所有 Promise 并行解析。

    Note

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

    等待 Promise 字典

    状态

    阶段:3

    推动者:

    作者:

    动机

    逐个属性使用 await 会产生瀑布效应,而不是并行运行请求:

    const obj = {
      shape: await getShape(),
      color: await getColor(),
      mass: await getMass(),
    };

    Promise.all 有所帮助,但它基于顺序而不是名称,这可能导致混淆:

    const [
      color,
      shape,
      mass,
    ] = await Promise.all([
      getShape(),
      getColor(),
      getMass(),
    ]);

    使用现有语法的解决方案可能冗长,并且会_污染_作用域中的变量数量:

    const shapeRequest = getShape();
    const colorRequest = getColor();
    const massRequest = getMass();
    
    const shape = await shapeRequest;
    const color = await colorRequest;
    const mass = await massRequest;

    此外,上述模式有未处理的 Promise 拒绝的风险。如果 await shapeRequest 抛出异常,那么没有处理程序附加到 colorRequestmassRequest 的 Promise 上——如果这两个之一拒绝,将导致未处理的 Promise 拒绝错误。在某些系统上,这会导致进程退出。

    提议的解决方案

    const {
      shape,
      color,
      mass,
    } = await Promise.allKeyed({
      shape: getShape(),
      color: getColor(),
      mass: getMass(),
    });

    有意遵循了 https://github.com/tc39/proposal-joint-iteration 的形式。

    正如 Promise.all 之于 Iterator.zip

    Iterator.zip = (Array<Iterator<T>>) => Iterator<Array<T>>
    Promise.all  = (Array<Promise<T>>)  => Promise<Array<T>>

    Promise.allKeyed 之于 Iterator.zipKeyed

    type Dict<V> = { [k: string | symbol]: V };
    
    Iterator.zipKeyed = <D extends Dict<Iterator<any>>>(iterables: D)
      => Iterator<{ [k in keyof D]: Nexted<D[k]> }>
    
    Promise.allKeyed  = <D extends Dict<Promise<any>>>(promises: D)
      => Promise <{ [k in keyof D]: Awaited<D[k]> }>

    附加 API

    本提案还将此特性扩展到了Promise.allSettled用户。

    const results = await Promise.allSettledKeyed({
        shape: getShape(),
        color: getColor(),
        mass: getMass(),
    });
    if (results.shape.status === "fulfilled") {
      console.log(results.shape.value);
    } else {
      console.error(results.shape.reason)
    }

    现有解决方案

    自有符号
    Bluebird.props
    combine-promises
    p-props

    实现

    Polyfill/转译器实现

    无。

    原生实现

    无。

    问答

    为什么没有深拷贝选项?

    除了 JSON.stringify,内置的 JavaScript API 不常见地深度遍历任意对象。

    Array.prototype.flat 是_深度的_,但仅限于明确定义的数组边界。

    为什么只处理自有属性?

    这遵循了其他内置函数,如 Object.keys,并且也与 https://github.com/tc39/proposal-joint-iteration 一致。

    符号键呢?

    所有可枚举属性都会被使用,包括可枚举的符号。

    这与 https://github.com/tc39/proposal-joint-iteration 一致。

    这与现有解决方案不同,后者遵循 Object.keys 的语义(忽略符号)。由于自有的可枚举符号使用频率较低,因此这种差异不构成问题。

    考虑的替代方案

    Promise.ownProperties

    const {
      shape,
      color,
      mass,
    } = await Promise.ownProperties({
      shape: getShape(),
      color: getColor(),
      mass: getMass(),
    });

    Promise.fromEntries

    const {
      shape,
      color,
      mass,
    } = await Promise.fromEntries(Object.entries({
      shape: getShape(),
      color: getColor(),
      mass: getMass(),
    }));

    Promise.all 重载

    根据参数是否为可迭代对象进行分派。

    const {
      shape,
      color,
      mass,
    } = await Promise.all({
      shape: getShape(),
      color: getColor(),
      mass: getMass(),
    });

    “这_确实_避免了在 API 表面引入新名称。然而,对于输出的形状依赖于输入的形状,以及意外传递多个参数而不是数组的风险,存在不适感。例如:

    Promise.all(p1, p2, p3); // ❌ 本应该是 `Promise.all([p1, p2, p3])`

    虽然目前这会因为 p1 不可迭代而抛出异常,但重载将开始允许这种调用,但不会执行调用者想要的意图。

    专用语法

    受其他语言(如 Swift)的启发 - 见:https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency#Calling-Asynchronous-Functions-in-Parallel

    async const shape = getShape();
    async const color = getColor();
    async const mass = getMass();
    
    const obj = await {
      shape,
      color,
      mass: Math.max(0, mass),
    };

    await <exp> 中对 async const 标识符的所有引用都会被隐式等待。

    上述代码大致等价于:

    const $0 = getShape();
    const $1 = getColor();
    const $2 = getMass();
    
    const obj = await ((shape, color, mass) => ({
      shape,
      color,
      mass: Math.max(0, mass),
    }))(await $0, await $1, await $2);