promise 本身不可终止,中断本质是放弃后续处理、提前拒绝并清理资源;应使用 abortcontroller 主动控制、封装可取消工厂函数或 promise.race 实现条件中断,避免误用悬停或普通错误模拟。

Promise 本身不可终止,但“中断挂起的 Promise”本质上是**放弃后续处理、提前拒绝、并清理相关资源**。关键不在于让已启动的异步任务物理停止(比如已发出的 fetch 请求无法撤回),而在于让代码逻辑及时响应取消意图,不再消费结果、不触发副作用、不浪费执行上下文。
用 AbortController 主动控制信号
这是现代标准方案,原生支持 fetch、setTimeout(需手动适配)、ReadableStream 等 API,语义清晰且可组合。
- 创建
AbortController实例,从中提取signal - 在 Promise 内部监听
signal.aborted状态或abort事件,一发现就调用reject - 调用
controller.abort()即可触发中断,所有监听该 signal 的 Promise 都会统一拒绝 - 注意:需主动清理定时器、事件监听器等资源,避免内存泄漏
封装可取消的 Promise 工厂函数
适用于不支持 AbortSignal 的老环境,或需要精细控制取消时机的自定义异步逻辑。
- 返回一个带
cancel()方法的对象,内部维护一个isCancelled标志 - Promise 执行体中定期检查标志,一旦为 true 就立即 reject
- 务必在 resolve/reject 前做一次最终检查,防止竞态条件
- 适合封装 setTimeout、WebSocket 连接、Canvas 动画帧等手动管理的任务
用 Promise.race 实现超时或条件中断
不依赖外部状态,靠竞争机制实现“被动中断”,适合超时、优先级切换等场景。
- 将目标 Promise 与一个“中断 Promise”(如
delay(3000).then(() => { throw new Error('timeout') }))一起传入Promise.race - 谁先 settle,整个 race 结果就定下来;中断 Promise 胜出即视为中断
- 可组合多个中断条件,例如 race([promise, userCancelSignal, timeoutSignal])
- 缺点是无法事后主动触发取消,只适用于预设条件触发的场景
中断 Promise 链的常见误区
中断不是让 Promise “停下来”,而是让链路提前进入 rejection 分支,并阻止后续 .then 执行。
- 不要试图在
.then中 return 一个 pending Promise 来“卡住”链路——这会导致悬停,不是中断 - 避免在链中抛出普通错误来模拟中断,容易和真实业务错误混淆;建议用自定义错误类(如
new CancellationError())区分 - 中断后仍要确保 UI 状态同步更新(如按钮恢复可用、加载指示器隐藏),否则用户感知不到操作已生效
- 多次调用
controller.abort()是安全的,重复调用不会报错











