资讯动态

Axios 请求取消机制详解:AbortController、CancelToken 兼容 API 与底层取消链路

发布时间:2026/9/7 8:59:58 来源:尧图企业网站定制
Axios 请求取消机制详解AbortController、CancelToken 兼容 API 与底层取消链路【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axiosAxios 从 v0.22.0 起通过AbortController提供了一套跨浏览器与 Node.js 的标准请求取消方案同时保留了处于弃用状态的CancelToken旧 API 用于兼容存量代码。本文基于仓库文档 cancellation.md逐条覆盖两种取消方式的用法、CanceledError错误识别与 TypeScript 类型收窄并结合 lib/cancel 与 lib/adapters 下的真实源码拆解信号从配置项一路传递到 XHR/fetch/http 适配器的完整取消链路帮助你在实际项目中安全地接入并排查请求取消问题。为什么取消请求是 HTTP 客户端的刚需在搜索联想、轮询接口、组件卸载如 React 的useEffectcleanup等场景中往往需要在响应返回之前主动放弃一次尚未完成的请求避免过期响应污染 UI 或浪费带宽。Axios 的取消机制解决的就是这个问题调用方只需把取消信号交给请求配置一旦触发取消进行中的请求会被尽快终止并最终以一个可识别的取消错误CanceledError拒绝 Promise而不是表现为普通网络错误或超时。Axios 目前提供两代 API能力AbortControllerCancelTokenDeprecated推荐状态官方推荐v0.22.0 起已弃用将在下一个大版本移除配置项signalcancelToken取消触发controller.abort()source.cancel(message)或 executor 捕获的cancel()跨多个请求复用支持支持平台要求运行环境需支持 AbortController现代浏览器与较新的 Node.js由 axios 自身实现兼容更老环境方式一推荐AbortController signal用法非常直接创建AbortController实例把它的signal传给请求的signal配置项需要取消时调用controller.abort()const controller new AbortController(); axios .get(/foo/bar, { signal: controller.signal, }) .then(function (response) { //... }); // cancel the request controller.abort();该 API 的优势在于它不是 axios 私有的而是 Web 标准fetch、WebSocket 等同样使用取消语义在所有现代浏览器和支持 AbortController 的 Node.js 版本中一致。底层原理信号如何进入适配器。请求进入调度阶段后axios 会把signal与各适配器自身的终止能力绑定XHR 适配器浏览器在 lib/adapters/xhr.js 中若存在config.signal会先检查signal.aborted——已处于 aborted 状态则立即中止否则监听abort事件调用xhr.abort()请求完成后会解绑监听removeEventListener(abort, onCanceled)防止泄漏。http 适配器Node.jslib/adapters/http.js 对config.signal做同样的处理触发时中止底层 Node 请求句柄并在请求结束时移除abort监听。fetch 适配器lib/adapters/fetch.js 通过composeSignals把signal与cancelToken如有合成一个信号再挂到 fetch 的signal选项上。也就是说signal并不只是被存储起来而是被真正接到了底层传输的中止开关上。方式二已弃用CancelTokenCancelTokenAPI 已弃用并将在下一个大版本移除官方建议迁移到AbortController。但存量代码中大量存在该用法理解它依然必要。1. 通过 CancelToken.source 工厂创建const CancelToken axios.CancelToken; const source CancelToken.source(); axios .get(/user/12345, { cancelToken: source.token, }) .catch(function (thrown) { if (axios.isCancel(thrown)) { console.log(Request canceled, thrown.message); } else { // handle error } }); axios.post( /user/12345, { name: new name, }, { cancelToken: source.token, } ); // cancel the request (the message parameter is optional) source.cancel(Operation canceled by the user.);对应源码见 lib/cancel/CancelToken.jssource()内部实际就是构造一个CancelToken把 executor 里收到的cancel函数闭包出来返回。source.cancel(message)调用时会向 token 写入一个CanceledError消息缺省为canceled并 resolve 内部 Promise 触发所有监听者。2. 通过 executor 函数创建const CancelToken axios.CancelToken; let cancel; axios.get(/user/12345, { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel c; }), }); // cancel the request cancel();从 CancelToken 构造函数 可以看到几个关键实现细节executor 必须传入函数否则抛出TypeError(executor must be a function.)构造时创建一个内部 Promise取消发生时token.reason被赋值为new CanceledError(message, config, request)随后 resolve 该 Promise所有subscribe过的监听者依次收到取消原因重复取消是幂等的若token.reason已存在后续的 cancel 调用直接返回不会二次触发。3. 低级辅助方法subscribe / unsubscribe / toAbortSignalCancelToken还为遗留集成暴露了低级辅助能力const source axios.CancelToken.source(); const listener (cancel) { console.log(cancel.message); }; source.token.subscribe(listener); const signal source.token.toAbortSignal(); // Pass signal to APIs that accept AbortSignal. source.cancel(Operation canceled by the user.); source.token.unsubscribe(listener);其中toAbortSignal()值得注意见 CancelToken.js 的实现它内部new AbortController()把取消原因通过subscribe(abort)桥接为controller.abort(err)并额外给返回的signal挂了一个unsubscribe方法用于反向解绑。这正是 axios 内部能同时接受cancelToken与signal两种配置、并在 fetch 适配器里用composeSignals合成统一信号的基础——也就是说弃用 API 与标准 API 在底层是互通的。subscribe(listener)有一个便利行为如果订阅时取消已经发生监听者会同步立即收到取消原因而不会错过事件见 CancelToken.js。识别取消CanceledError 与 axios.isCancel被取消的请求会以axios.CanceledError拒绝。查看 lib/cancel/CanceledError.jsCanceledError继承自AxiosError错误码为AxiosError.ERR_CANCELED默认消息为canceled实例上带有name CanceledError与__CANCEL__ true两个标记。其中__CANCEL__是历史兼容位旧版 axios 用axios.isCancel判断取消其实现lib/cancel/isCancel.js只检查value.__CANCEL__这一标志位export default function isCancel(value) { return !!(value value.__CANCEL__); }因此旧代码axios.isCancel(err)对新版抛出的CanceledError依然有效。此外legacy 的axios.Cancel导出是axios.CanceledError的别名方便平滑迁移。单元测试 tests/unit/cancel/canceledError.test.js 也验证了默认/自定义消息的toString()输出CanceledError: canceled/CanceledError: message。调度层的兜底检查。除适配器内的中止外axios 在请求调度边界还会做两次主动检查见 lib/core/dispatchRequest.js 的throwIfCancellationRequested请求发出前若config.cancelToken已请求取消则调用throwIfRequested()直接抛出若config.signal.aborted为真则立即抛出CanceledError——即发起时已取消的请求根本不会真正发出去后文详述请求完成后adapter resolve 时与失败时非取消原因再次检查保证响应处理阶段的竞态也能被识别为取消。TypeScriptisCancel 的泛型重载在 TypeScript 项目中axios.isCancel提供了泛型签名可以在把unknown类型的 catch 错误收窄为取消错误的同时保留响应数据、请求体和查询参数的类型interface SearchResponse { results: string[]; } interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } try { await axios.get(/search); } catch (error) { if (axios.isCancelSearchResponse, RequestBody, SearchParams(error)) { error.response?.data; // SearchResponse | undefined error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } }三个类型参数分别对应isCancelT, D, P()的响应数据类型、请求数据类型与查询参数类型签名定义可参考仓库根目录的 index.d.ts。这避免了取消分支中大量as断言。信号合成一个请求中同时有 timeout、signal 与 cancelToken 时真实配置里timeout、signal、cancelToken可能同时存在。axios 用 lib/helpers/composeSignals.js 将它们合并为一个内部AbortController的信号收集所有真值信号signals.filter(Boolean)若既无信号也无 timeout 则直接返回不创建多余对象timeout到期时以AxiosError(..., ETIMEDOUT)触发合成信号 abort任一外部信号触发abort或CancelToken的unsubscribe风格取消回调时立即 abort 合成信号并把原因包装成CanceledError若原因本身是AxiosError则原样透传同时通过unsubscribe()一次性清除所有监听与定时器保证不泄漏若某个传入的signal在注册时已经 aborted会立刻同步触发onabortcomposeSignals.js。合成后的signal还挂了unsubscribeutils.asap(unsubscribe)供适配器在请求结束时调用及时释放监听。fetch 适配器即通过该机制把signal、cancelToken.toAbortSignal()与超时统一汇入 fetch 的signal选项见 lib/adapters/fetch.js。实用行为一个 token 取消多个请求 / 已取消的 token 立即拒绝两个值得记住的行为均来自 cancellation.md同一 token/controller 可取消多个请求。CancelToken.source()返回的token或同一个controller.signal可以同时传给多个 axios 请求调用一次cancel()/abort()全部作废。这在翻页时取消所有在途的旧请求场景中非常实用。token 已取消时新请求立即取消。如果取消 token 在请求发起时刻已被取消axios 会直接以取消错误拒绝 Promise不会发起任何真实网络请求。这一点由 dispatchRequest.js 的预检逻辑与 composeSignals.js 对已 aborted 信号的同步处理共同保证源码层面可验证。一个基于 AbortController 的取消在途请求典型写法// 每次搜索前先取消上一次未完成的请求 let controller null; async function search(query) { controller?.abort(); controller new AbortController(); try { const { data } await axios.get(/search, { params: { query }, signal: controller.signal, }); return data; } catch (err) { if (axios.isCancel(err)) { // 被主动取消静默处理即可 return; } throw err; } }小结新代码一律使用AbortControllersignal配置项它是 Web 标准浏览器与 Node.js 通用CancelTokensource()/ executor /subscribe/toAbortSignal已弃用但当前仍可用且通过toAbortSignal()与标准信号互通存量代码可渐进迁移取消错误统一为CanceledErrorERR_CANCELED、__CANCEL__用axios.isCancel判断TS 下可用isCancelT, D, P()收窄类型取消信号在 dispatchRequest.js 的预检/后检、composeSignals.js 的信号合成以及各适配器xhr.js、http.js、fetch.js的事件绑定三层共同生效已取消 token 发起请求立即拒绝的行为有明确的源码依据。【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价