news 2026/9/7 8:59:43

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Axios 请求取消机制详解:AbortController、CancelToken 兼容 API 与底层取消链路

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

【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios

Axios 从 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:

能力AbortControllerCancelToken(Deprecated)
推荐状态官方推荐(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.js):lib/adapters/http.js 对config.signal做同样的处理,触发时中止底层 Node 请求句柄,并在请求结束时移除abort监听。
  • fetch 适配器:lib/adapters/fetch.js 通过composeSignalssignalcancelToken(如有)合成一个信号,再挂到 fetch 的signal选项上。

也就是说,signal并不只是被存储起来,而是被真正接到了底层传输的中止开关上。

方式二(已弃用):CancelToken

CancelTokenAPI 已弃用并将在下一个大版本移除,官方建议迁移到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.js:source()内部实际就是构造一个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 / toAbortSignal

CancelToken还为遗留集成暴露了低级辅助能力:

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 内部能同时接受cancelTokensignal两种配置、并在 fetch 适配器里用composeSignals合成统一信号的基础——也就是说,弃用 API 与标准 API 在底层是互通的。

subscribe(listener)有一个便利行为:如果订阅时取消已经发生,监听者会同步立即收到取消原因,而不会错过事件(见 CancelToken.js)。

识别取消:CanceledError 与 axios.isCancel

被取消的请求会以axios.CanceledError拒绝。查看 lib/cancel/CanceledError.js:

  • CanceledError继承自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

  1. 请求发出前:若config.cancelToken已请求取消则调用throwIfRequested()直接抛出;若config.signal.aborted为真则立即抛出CanceledError——即"发起时已取消的请求根本不会真正发出去"(后文详述);
  2. 请求完成后(adapter resolve 时)与失败时(非取消原因):再次检查,保证响应处理阶段的竞态也能被识别为取消。

TypeScript:isCancel 的泛型重载

在 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.isCancel<SearchResponse, RequestBody, SearchParams>(error)) { error.response?.data; // SearchResponse | undefined error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } }

三个类型参数分别对应isCancel<T, D, P>()的响应数据类型、请求数据类型与查询参数类型,签名定义可参考仓库根目录的 index.d.ts。这避免了取消分支中大量as断言。

信号合成:一个请求中同时有 timeout、signal 与 cancelToken 时

真实配置里timeoutsignalcancelToken可能同时存在。axios 用 lib/helpers/composeSignals.js 将它们合并为一个内部AbortController的信号:

  • 收集所有真值信号(signals.filter(Boolean)),若既无信号也无 timeout 则直接返回(不创建多余对象);
  • timeout到期时以AxiosError(..., ETIMEDOUT)触发合成信号 abort;
  • 任一外部信号触发abort(或CancelTokenunsubscribe风格取消回调)时,立即 abort 合成信号,并把原因包装成CanceledError(若原因本身是AxiosError则原样透传),同时通过unsubscribe()一次性清除所有监听与定时器,保证不泄漏;
  • 若某个传入的signal在注册时已经 aborted,会立刻同步触发onabort(composeSignals.js)。

合成后的signal还挂了unsubscribeutils.asap(unsubscribe)),供适配器在请求结束时调用,及时释放监听。fetch 适配器即通过该机制把signalcancelToken.toAbortSignal()与超时统一汇入 fetch 的signal选项(见 lib/adapters/fetch.js)。

实用行为:一个 token 取消多个请求 / 已取消的 token 立即拒绝

两个值得记住的行为(均来自 cancellation.md):

  1. 同一 token/controller 可取消多个请求。CancelToken.source()返回的token或同一个controller.signal可以同时传给多个 axios 请求,调用一次cancel()/abort()全部作废。这在"翻页时取消所有在途的旧请求"场景中非常实用。
  2. 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; } }

小结

  • 新代码一律使用AbortController+signal配置项;它是 Web 标准,浏览器与 Node.js 通用;
  • CancelTokensource()/ executor /subscribe/toAbortSignal)已弃用但当前仍可用,且通过toAbortSignal()与标准信号互通,存量代码可渐进迁移;
  • 取消错误统一为CanceledErrorERR_CANCELED__CANCEL__),用axios.isCancel判断;TS 下可用isCancel<T, 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 8:58:47

C#上位机集成libNFC:NFC读卡器封装实战与工业应用

简介&#xff1a;面向需要在C#中调用libNFC实现近场通信&#xff08;NFC&#xff09;功能的.NET开发者&#xff0c;提供了一套可直接参考的多项目解决方案&#xff0c;围绕P/Invoke跨平台调用、设备发现与连接、标签读写、NDEF消息处理、事件驱动和资源释放等核心环节展开&…

作者头像 李华
网站建设 2026/9/7 8:58:22

湖南单招学考成绩重要吗,考差了怎么办?湘楚有才单招官方深度解析

湘楚有才单招全国咨询热线:400‑893‑7001,湘楚有才单招官方咨询专线:13203104897前言每年湖南单招备考季,家长问得最多的核心问题就是:孩子学考成绩一般、甚至考得比较差,还能不能走单招上岸公办大专?湖南单招学考成绩到底重不重要?学考低分有没有补救办法、翻盘机会?绝大多…

作者头像 李华
网站建设 2026/9/7 8:57:22

DeepSeek Harness实战:API接入、Agent边界与多模态识图方案

很多人第一次接触 DeepSeek&#xff0c;是从网页对话开始的。输入一段提示词&#xff0c;模型给你一段回答&#xff0c;体验不错&#xff0c;但真把它放进自己的工作流里&#xff0c;立刻会遇到几种尴尬&#xff1a;页面之外没法用、图片传进去没反应、想让它操作本地代码或文档…

作者头像 李华
网站建设 2026/9/7 8:56:38

自研BACnet设备模拟器,解决楼宇自控联调难题

简介&#xff1a;这是一份BACnet模拟器及配套源码工具包&#xff0c;面向楼宇自控工程师、系统集成商及BACnet协议初学者&#xff0c;用于在没有实体设备的情况下完成设备模拟、网络调试、协议验证与故障排查。资源共1650个文件&#xff0c;总大小约103.61MB&#xff0c;核心为…

作者头像 李华
网站建设 2026/9/7 8:56:28

深度学习入门实战:基于CNN的验证码识别完整项目

简介&#xff1a;一套完整的字符型图片数字验证码识别项目资料&#xff0c;基于深度学习技术实现&#xff0c;面向正在学习图像识别、神经网络或网络安全反自动化攻防的开发者。内容覆盖验证码数据集构建、图像预处理、卷积神经网络与循环神经网络模型搭建、训练评估及Python推…

作者头像 李华