Appearance
2650. 设计可取消函数
ts
function cancellable<T>(
generator: Generator<Promise<any>, T, unknown>
): [() => void, Promise<T>] {
let cancelled = false
const cancel = () => {
cancelled = true
}
const promise = new Promise<T>(async (resolve, reject) => {
try {
let result = generator.next()
while (!result.done) {
await result.value
if (cancelled) return reject('Cancelled')
result = generator.next()
}
if (cancelled) return reject('Cancelled')
resolve(result.value)
} catch (err) {
if (cancelled) return reject('Cancelled')
reject(err)
}
})
return [cancel, promise]
}为什么用 Generator
这个问题的输入是一个 Generator 函数,它 yield 出 Promise。Generator 天然支持「执行一步 → 等待 → 检查状态 → 继续执行」,恰好对应取消语义的实现需求。
如果不使用 Generator,在普通的 Promise 链中插入取消检查会非常困难——Promise 一旦创建就无法从外部中止。Generator 把异步流程的控制权交还给了调用方代码,使得每一步之后都可以插入取消判断。
取消的时机
设计上有两个检查点:
- 每个
yield出的 Promise resolve 之后 - Generator 完全执行完毕时
这意味着取消不中止正在飞行的 Promise——已经 await 的 Promise 会继续跑完。取消只阻止后续步骤的执行。这和浏览器中 AbortController 的行为一致——真正的网络请求取消需要底层支持(fetch 配合 signal),而不是封装层能做到的。
错误处理
catch 分支也需要检查 cancelled。如果 Generator 内部抛错,但外部已经取消了,应该返回 Cancelled 而不是抛原始错误——因为调用方已经不关心结果了。
