Promise 怎么取消?AbortController 够用吗?

用户切换了页面,请求已经没人关心了,但那个还在跑的 async 函数并不知道。

Promise 本身没有取消的概念,常用的 AbortController 能用,但它是约定,不是机制,漏传一处,取消就在那一处失效。 Effect 的做法是让中断成为运行单位 fiber 的内置能力,不需要手动传 signal。

先看约定具体长什么样,就知道会在哪里漏。

用 AbortController 取消是什么样子?

async function getUser(id: string, signal?: AbortSignal): Promise<User> {
  const res = await fetch(url, { signal })
  signal?.throwIfAborted()
  // ...每一步都要记得检查和传递
}

每个函数都得多收一个参数,每个下游调用都得记得往下传。

它哪里会漏?

漏传一处,取消就在那一处失效。签名里没有任何东西强制你传,所以漏了也编译通过。

资源清理更麻烦:取消发生在两个 await 之间时,已经打开的连接、已经开始的事务由谁来关?靠 try/finally,而且要写对。

Effect 里的取消有什么不同?

Effect 的运行单位叫 fiber。中断是 fiber 的内置能力,不需要手动传 signal。 一个 Effect 被中断时,它已经注册的清理动作会被运行,嵌套的子任务会被一起中断。

所以取消从「约定」变成了「机制」。fiber 的细节见 Fiber,清理动作怎么注册见 资源安全。

这是不是说明我该立刻换成 Effect?

不一定。取消只是 async 函数签名看不见的三件事之一,另外两件是失败方式和依赖,Effect 的收益要放在一起算,也要把学习成本和协作成本算进去,完整讨论见 已经有 async/await 了,为什么还要用 Effect?。

本文的 API 名称以 effect 4.0.5 为准。代码是说明用的示意,我没有运行过;关于中断时清理动作运行、子任务一并中断的描述只是概括,没有自己验证,细节以对应章节为准。