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/year/pending/proposal-symbol-thenable.md.
  • 简体中文
  • Symbol.thenable ?

    提案概览
    提案速览

    该提案引入了 Symbol.thenable 以防止 Promise.resolve 将某些对象视为 thenable,特别是动态导入时的模块命名空间对象。它解决了模块命名空间被错误解释为 thenable 时可能出现的意外行为。resolve 中白名单化模块命名空间对象的替代方案。

    Note

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

    Symbol.thenable

    Champions: Myles Borins, Jordan Harband

    Author: Gus Caplan

    Stage 0

    为什么

    Promise.resolve 的行为包括检查你传递给它的任何对象的 then 函数。这允许与遗留的第三方 Promise 实现协议进行互操作,在大多数情况下不会导致任何问题,但仍然存在一些不必要的场景。

    这在动态导入模块命名空间对象时尤其如此,这很快将成为语言规范的一部分。命名空间可能被解释为"thenable",导致 import() 返回非命名空间对象——这可能非常出乎意料。之前围绕这些场景进行了大量讨论,包括 https://github.com/tc39/proposal-dynamic-import/issues/47https://github.com/tc39/proposal-dynamic-import/issues/48。

    import * as static from 'X'
    
    import('X').then((dynamic) => {
      assert(static === dynamic); // 可能为 false,/希望/ X 的使用者和 X 的作者
                                  // 都知道此行为(如果他们知道,没人会利用
                                  // 它来做令人困惑的事情)
    });

    提案

    对此问题的自然结论是为对象添加某种修饰符,使得 Promise.resolve 知道不应执行"thenable"行为。

    引入:Symbol.thenable

    Promise.resolve({
      [Symbol.thenable]: false,
      then() { return 'a time that isn\'t now'; },
    }).then((o) => {
      o.then() === 'a time that isn\'t now';
    });

    此外,此符号将默认设置在模块命名空间对象上。虽然用户已经可以通过 Promise 解析来 import * 一个命名空间对象并返回它,但今天这仍然可以被认为是非常罕见的情况。

    这源于尝试思考该问题的最通用解决方案。替代方案可能包括在 Promise.resolve 中显式地将模块命名空间对象列入黑名单,但对此存在阻力,因为可能不完全直观为什么这个对象要被区别对待。Symbol.thenable 为此提供了清晰的解释。

    将来,此符号还可以被协议使用(参见 第一类协议提案),沿着 Promise.Thenable 的思路作为一种"禁用"符号。

    替代方案和选项

    • 将符号挂载在 Promise 命名空间上(Promise.thenable

    • 将我们不想视为"thenable"的特定对象列入白名单:

      1. 如果 _resolution_ 是模块命名空间对象,则
        1. 返回 FulfillPromise(_promise_, _resolution_)。