首页
/ Axios 请求取消机制详解:AbortController 与 CancelToken 的完整实践

Axios 请求取消机制详解:AbortController 与 CancelToken 的完整实践

2026-09-05 14:49:36作者:齐添朝

从 v0.22.0 开始,Axios 支持通过 AbortController 以标准 Web 方式干净地取消请求;对于旧代码,仓库仍保留了已标记为废弃的 CancelToken API(将在下一个主版本中移除)。读完本文,你将掌握两种取消方式的完整用法、取消错误(CanceledError)的识别与类型收窄技巧,以及信号在 dispatchRequest 和各平台 adapter 中的实际接线方式——从浏览器 XHR 到 Node.js HTTP 适配器。

使用 AbortController 取消请求(推荐方式)

AbortController 是浏览器与 Node.js 的 Web 标准 API,Axios 通过请求配置的 signal 选项与其对接。基本用法是:创建一个 AbortController 实例,把它的 signal 传给请求,需要取消时调用 controller.abort()

const controller = new AbortController();

axios
  .get('/foo/bar', {
    signal: controller.signal,
  })
  .then(function (response) {
    //...
  });
// cancel the request
controller.abort();

这种方式的价值在于:AbortSignal 是跨生态的标准信号对象,可以同时在 fetch、WebSocket、Node 的 http 请求以及第三方库之间复用,无需绑定 Axios 私有的取消机制。从仓库源码可以看到,signal 会被两个层面消费:

  1. 调度前检查dispatchRequest 中的 throwIfCancellationRequested(config) 会在发出请求前检查 config.signal.aborted,若已中止则直接抛出 CanceledError,不产生真实网络请求:
function throwIfCancellationRequested(config) {
  if (config.cancelToken) {
    config.cancelToken.throwIfRequested();
  }

  if (config.signal && config.signal.aborted) {
    throw new CanceledError(null, config);
  }
}
  1. adapter 运行时监听:在请求飞行期间,adapter 会订阅 abort 事件。浏览器端 xhr.js 中可以看到对 cancelTokensignal 的双通道处理:
if (_config.cancelToken || _config.signal) {
  // Add a cleanup callback to remove the abort event listener
  _config.cancelToken && _config.cancelToken.subscribe(onCanceled);
  if (_config.signal) {
    _config.signal.aborted
      ? onCanceled()
      : _config.signal.addEventListener('abort', onCanceled);
  }
}

Node.js 端 http.js 采用了完全一致的模式,并在请求结束(onReqResponse 等路径)时通过 removeEventListener / unsubscribe 清理监听器,避免信号对象上的监听器泄漏。

此外,composeSignals 工具会把 timeout 产生的定时器信号与用户传入的 signal 合并为一个统一的 AbortController 信号供 adapter 内部使用——这意味着 timeoutsignal 在底层走的是同一条中止链路:超时触发 AxiosError(ETIMEDOUT),外部取消则触发 CanceledError

使用 CancelToken 取消请求(已废弃)

文档明确标注了 CancelToken 为 Obsoleto(已废弃):它仍可用,但将在下一个主版本中移除,新代码建议使用 AbortController。它有两种创建方式。

方式一:CancelToken.source() 工厂

通过静态方法 CancelToken.source() 创建一个 { token, cancel } 对,同一个 token 可以同时传给 GET 和 POST:

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.');

CancelToken.js 的源码看,source() 本质上是用一个闭包保存执行器注入的 cancel 函数:

static source() {
  let cancel;
  const token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token,
    cancel,
  };
}

方式二:通过执行器函数构造

CancelToken 构造函数传入执行器函数,函数参数即为取消函数,可在任意时机保存并调用:

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.js)值得注意三点:

  • 执行器参数必须是函数,否则抛出 TypeError
  • 内部持有一个 promise,执行器调用 cancel(message, config, request) 时,若尚未取消过(token.reason 为空),会创建 CanceledError 并 resolve 该 promise,随后遍历通知所有已订阅的监听器;重复调用 cancel 会被忽略,保证取消只发生一次;
  • 被覆写的 promise.then 实现了「链式订阅」:.then 回调只会在取消时触发,且返回的 promise 带有 cancel() 方法用于退订。

CancelToken 的低层辅助 API(面向遗留集成)

除了面向请求的用法,CancelToken 还暴露了几个低层 helper,用于旧框架或自定义封装的集成场景(原文档给出的完整示例):

const source = axios.CancelToken.source();

const listener = (cancel) => {
  console.log(cancel.message);
};

source.token.subscribe(listener);

const signal = source.token.toAbortSignal();
// Pasa `signal` a APIs que acepten AbortSignal.(把 signal 传给接受 AbortSignal 的 API)

source.cancel('Operation canceled by the user.');
source.token.unsubscribe(listener);

对应源码行为:

  • subscribe(listener) / unsubscribe(listener)CancelToken.js):注册/移除取消监听器。若订阅时 token 已被取消,监听器会同步立即收到 reason(即 CanceledError),无需等待任何异步事件;
  • toAbortSignal()CancelToken.js):内部 new AbortController() 并把 abort 回调订阅到 token 上,取消时以 controller.abort(err) 携带原因中止。返回的 signal 额外挂了 unsubscribe 方法用于退订——这意味着废弃的 CancelToken 可以桥接到接受标准 AbortSignal 的第三方 API,是向 AbortController 世界迁移时的关键过渡工具;
  • throwIfRequested()CancelToken.js):若 reason 已存在则抛出它。dispatchRequest 正是靠它在请求发出前完成「立即取消」的判定。

取消错误:CanceledError 与 isCancel

被取消的请求会以 axios.CanceledError 拒绝。查看 CanceledError.js 的实现:

class CanceledError extends AxiosError {
  constructor(message, config, request) {
    super(message == null ? 'canceled' : message, AxiosError.ERR_CANCELED, config, request);
    this.name = 'CanceledError';
    this.__CANCEL__ = true;
  }
}

关键点:

  • 它继承自 AxiosErrorcodeERR_CANCELEDmessage 缺省为 'canceled'
  • 实例上带有 __CANCEL__ = true 标志,这是 isCancel 的判定依据——整个函数体只有一行:return !!(value && value.__CANCEL__);
  • 遗留的 axios.Cancel 导出是 axios.CanceledError 的别名,保证旧代码中 instanceof axios.Cancel 的写法不破裂。

仓库的测试用例(canceledError.test.js)验证了两条行为:toString() 在有无 message 时分别输出 CanceledError: canceled 与自定义消息;且 CanceledError 能被 Node 的 util.types.isNativeError 识别为原生错误,这在生产环境依赖堆栈/错误上报的场景中是有意义的。

TypeScript:isCancel 的类型收窄

在 TypeScript 中,isCancel<T, D, P>() 可以收窄 unknown 错误类型,同时保留响应数据、请求体与查询参数的泛型:

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
  }
}

这在处理「取消后仍需要读取已到达的部分响应」或审计取消原因(访问 error.config)时非常实用。

多请求共享 token 与「启动即取消」

你可以用同一个取消 token(或同一个 AbortController)取消多个请求。还有一个容易被忽略但重要的语义:如果 token 在 Axios 请求启动时就已经处于已取消状态,该请求会被立即取消,不会尝试发起任何真实请求

这一点在源码中有明确落点:dispatchRequest 的第一步就是 throwIfCancellationRequested(config)dispatchRequest.js),它先检查 cancelToken.throwIfRequested(),再检查 signal.aborted;即使请求已经发出,adapter 返回后的 onAdapterResolution / onAdapterRejection 路径里也会再次调用 throwIfCancellationRequesteddispatchRequest.js),确保「响应回来前一刻的取消」同样以 CanceledError 呈现,而不是让一个已废弃的响应被当作成功结果交付给业务代码。

迁移建议小结

结合文档标注与源码现状,实践上可以按以下优先级处理:

场景 推荐做法
新代码 / 新库集成 一律使用 AbortController + signal 选项
旧代码使用 cancelToken 继续可用,但规划迁移;可用 token.toAbortSignal() 与标准 API 桥接
需要监听取消事件 迁移前用 token.subscribe(listener),迁移后改为 signal.addEventListener('abort', ...)
错误分支判断 优先 axios.isCancel(err)(基于 __CANCEL__ 标志),TypeScript 中配合泛型 isCancel<T, D, P> 收窄类型
timeout 共存 无需额外处理:composeSignals 已在底层合并两者,取消报 CanceledError,超时报 AxiosError(ETIMEDOUT)

相关实现与测试入口:CancelToken.jsCanceledError.jsisCancel.jscomposeSignals.jsdispatchRequest.js,以及单元测试 canceledError.test.jsisCancel.test.js

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384