掌握JS请求取消机制的关键是理解“谁发起、谁负责、何时终止”:必须用AbortController(非XMLHttpRequest.abort或CancelToken),每次请求新建实例并传signal,组件卸载或路由跳转时调用abort,错误需显式捕获AbortError。

掌握 JS 请求取消机制,关键不是背 API,而是理解“谁发起、谁负责、何时终止”这个逻辑链条。从能手动 abort 一个 fetch,到在复杂场景下不漏不误不崩,需要分层推进。
先搞懂核心对象:AbortController 是唯一主线
现代浏览器中取消请求只靠 AbortController,它不是可选项,是标准解法。XMLHttpRequest.abort() 和 axios 的 CancelToken 都已过时或被标记废弃。必须把 AbortController.signal 当作请求的“生命线”来对待:
- 每次发请求前,新建
new AbortController(),别复用 - 把它的
.signal明确传给fetch、axios(v0.27+ 或 v1.0+)、WebSocket等支持 signal 的 API -
controller.abort()一调,所有绑定该 signal 的请求立刻中断,抛出AbortError - 调用 abort 后再调一次也安全,不会报错,适合清理逻辑写得宽松些
从单个请求开始练手感
不要一上来就封装工具函数。先手写三遍带取消的 fetch:
- 写一个搜索框输入事件,每次输入都新建 controller、abort 上一个、发新请求
- 写一个按钮点击加载用户数据,点击“取消”时调用 abort,并在 catch 中过滤
err.name === 'AbortError' - 写一个页面级加载,组件卸载时(React useEffect cleanup / Vue onUnmounted)自动 abort
这三步做完,你会自然形成肌肉记忆:controller 要存哪、abort 该在哪写、错误怎么判、为什么不能漏掉 cleanup。
进阶场景靠结构化管理,不是堆代码
真实项目里取消不是零散操作,而是有上下文的生命周期行为:
- 多步骤表单:每步独立 controller,切换步骤前 abort 当前 controller;不用共享一个,避免误杀
- 并发请求统一收口:比如首页加载用户+订单+通知,共用一个 controller.signal,点“刷新”就 abort 全部
-
路由跳转拦截:用 Map 存
to.path → controller,beforeEach里查旧路径并 abort;再加组件卸载 cleanup 双保险 - 文件下载控制:fetch + blob 方式可取消;但 a 标签 download 不可取消,这点必须分清
工程化落地要闭环,不能只管发不管收
大型应用里,取消逻辑必须变成可追踪、可调试、可收敛的设计:
- 封装基础 request 函数,接受可选
signal参数,不强制依赖全局状态 - 在状态管理中维护 pending 请求 map(如 key = method:url:hash),提供
cancelByPattern或cancelAll - 对重复请求做拦截:相同参数的搜索,新请求来时自动 cancel 旧的,再发新的
- 所有 abort 错误必须显式捕获,绝不让
AbortError泄露到全局错误监控里
学到这里,你就不是在“取消请求”,而是在设计请求的生命周期契约。


















