Cancellation API S1
中文标题:取消 API
- 阶段: Stage 1
- 状态: 进行中
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
本提案旨在定义一种原生 API,用于用户控制的异步操作取消,为 fetch、迭代和其他异步任务提供通用机制。它引入了取消令牌和取消源的概念,强调关注点分离和尽力而为的取消。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
ECMAScript 取消
本提案旨在通过采用一组原生平台对象,定义一种用户控制的异步操作取消方法。
状态
阶段: 1
推动者: Ron Buckton (@rbuckton), James M. Snell (@jasnell)
有关更多信息,请参阅 TC39 提案流程。
注意:TC39 已决定在核心库中研究取消机制。 因此,取消提案已进入第 1 阶段,但并非以前第 0 阶段提案的形式。 相反,TC39 认为这是一个需要进一步研究和讨论的领域。 之前的第 0 阶段提案可在此处找到。
作者
- Ron Buckton (@rbuckton)
动机
- 一种清晰且一致的方法来取消异步操作:
- 一个具有多种用例的通用协调机制:
- 同步观察(例如在游戏循环中)
- 异步观察(例如中止 XMLHttpRequest、停止动画)
- 易于在异步函数中使用。
- 一种在多种主机环境中可重用的通用 API(浏览器、NodeJS、物联网/嵌入式等)。
先例
- .NET 框架中的托管线程取消
架构
以下是由 Dean Tribble 在 es-discuss 邮件列表 上提供的一些架构观察:
取消请求,而非结果
Promise 就像异步的对象引用;任何特定的 Promise 都可能被返回或传递给多个客户端。通常,如果返回或传入的引用被另一个客户端突然夺走,程序员会感到惊讶。这在考虑库接收到传入的 Promise 时尤其明显。在 Promise 上使用 "cancel" 就像在对象引用上使用 delete 一样;使用起来很危险,被他人使用也不可靠。取消是异构的
考虑取消单个活动可能会产生误导。在大多数系统中,当取消发生时,可能由于相同原因需要取消许多不相关的任务。例如,如果用户在一个大型增量查询中看到前几个结果后点击停止按钮,应该发生什么?
- 应终止对更多查询结果的异步获取并关闭连接
- 应停止将远程结果处理为可渲染形式的后台计算
- 应停止渲染尚未渲染的内容。这可能包括检索不再感兴趣项目的次要内容(例如,通过复杂内容搜索找到的歌曲的专辑封面)
- 应停止“加载更多”的动画,并替换为“用户已取消”
- 等等。
其中一些是不同抽象级别,对于任何不平凡的应用,没有一个代码片段能够知道如何终止所有这些活动。这种系统还要求取消支持在许多不同类型的组件中保持一致。但如果每个活动都接受一个 cancellationToken,在上述示例中,它们只需要传递用户点击停止时将被取消的那个令牌,然后正确的事情就会发生。
取消应该是智能的
库可以并且应该智能地处理如何取消。在异步查询的情况下,一旦从服务器返回查询结果,完成解析并缓存它可能是有意义的,而不是被动地丢弃它。例如,在经纪系统中,往返服务器获取最近数据是昂贵的部分。一旦启动并且结果即将返回,在本地缓存中保留它以便用户再次请求时是高效的。如果应用程序派生了另一个 worker,让 worker 完成(以便可以重用)可能比突然终止它(需要丢弃正在运行的 worker 和缓存状态)更高效。取消是一场竞赛
在异步系统中,新的活动可能被持续调度,而这些调度本身是已调度但未运行的。取消的行为需要在这种环境中运行。当取消开始时,你可以将其视为信号竞赛以赶上所有为现已取消的目标而启动的计算。其中一些可能选择完成(参见上面的缓存示例)。有些可能在这些工作本身收到信号之前继续启动更多工作(是的,这是 bug 但人们会编写有 bug 的代码)。在异步系统中,取消不是即时的。因此,询问“取消是否已完成?”是不可行的,因为这不是一个定义良好的状态。实际上,可能存在已调度且不应被取消的代码(例如,发布/订阅系统的结果处理器),但这些代码会调度将被取消的工作(解析现已取消的查询的更新发布)。取消是“不再关心”
因为智能取消有时不会停止任何东西,而在异步环境中,取消与进度竞赛,它最多是“尽力而为”。当一组计算被取消时,取消活动的一方是在说“我不再关心这能否完成”。这与说“我想阻止它完成”有重要区别。前者是广泛可用的资源减少。后者只有在围绕原子性和事务进行昂贵工程的系统中才能有效实现。令人惊讶的是,当取消逻辑变为“不再关心”时,它会变得多么简单。取消需要关注点分离
在多个事物被取消的模式中,取消的来源很少是被取消的事物之一。如果一个库调用了一个可取消活动(加载此图像)却因为它们关心相同的取消事件而取消了一个无关的服务器查询,那将令人惊讶。我发现有趣的是,取消令牌和取消源之间的分离反映了 Promise 与其解析器之间的分离。取消恢复是瞬态的
随着任务的进展,清理操作可能会改变。在上面的示例中,如果数据表在滚动时请求更多结果,它在有未处理的更多数据查询时的取消行为很可能不同于当它已显示当前页面所需的所有内容时。这就是为什么“register”方法返回一个能够注销操作的 capability。
待办事项
以下是推进 TC39 提案流程 每个阶段的高级任务列表:
第 1 阶段进入标准
第 2 阶段进入标准
- 初始规范文本。
- 可选。 Transpiler 支持(用于语法)或 Polyfill(用于 API)。