Axios 取消请求的演进:CancelToken vs. AbortController

CancelToken(早期 Axios 取消方案)

Axios 早期使用 CancelToken 来实现请求取消,其核心是一个基于 Promise 的取消机制。我之前的文章有写

随着 Fetch API 的普及,浏览器引入了 AbortController,Axios 从 v0.22.0 开始支持该方案。下面就针对最新的AbortController做分享。

1. AbortController取消请求的基本原理

Axios 使用 AbortController 来实现请求取消功能。其核心机制是:

  1. 为每个请求创建一个 AbortController 实例
  2. 将控制器的 signal 附加到请求配置中
  3. 需要取消时调用控制器的 abort() 方法

2. 代码实现解析

2.1 请求拦截器设置

在 request.js 中,我们设置了请求拦截器来管理请求取消:

javascript 复制代码
// 存储所有活动的 AbortController
const activeRequests = new Map();

request.interceptors.request.use((config) => {
  const requestId = generateRequestId(config);
  
  // 取消重复请求
  if (activeRequests.has(requestId)) {
    activeRequests.get(requestId).abort('取消重复请求');
  }
  
  // 创建并存储新的控制器
  const controller = new AbortController();
  activeRequests.set(requestId, controller);
  
  // 附加 signal 到请求
  config.signal = controller.signal;
  
  return config;
});

2.2 响应拦截器清理

请求完成后需要清理控制器:

javascript 复制代码
request.interceptors.response.use((response) => {
  const requestId = generateRequestId(response.config);
  activeRequests.delete(requestId); // 清理控制器
  return response;
}, (error) => {
  // 错误处理
});

2.3 生成唯一请求ID

为了正确识别和取消特定请求,我们需要为每个请求生成唯一ID:

javascript 复制代码
export function generateRequestId(config) {
  const params = JSON.stringify(config.params || {});
  const data = JSON.stringify(config.data || {});
  return `${config.url}-${config.method.toLowerCase()}-${params}-${data}`;
}

2.4 手动取消请求

提供手动取消请求的接口:

javascript 复制代码
export function cancelRequest(requestId) {
  if (activeRequests.has(requestId)) {
    activeRequests.get(requestId).abort('用户手动取消');
    activeRequests.delete(requestId);
  }
}

3. 实际应用示例

3.1 流式请求中的取消

在 aiAgent.js 中,我们实现了流式请求的处理和取消:

javascript 复制代码
export function qwenTalk(data, onProgress) {
    const config = {
        url: '/api/chat',
        method: 'POST',
        data,
        responseType: 'text'
    };
    currentRequestConfig = config;
    
    // 重置状态
    buffer = '';
    lastPosition = 0;
    
    return request({
        ...config,
        onDownloadProgress: (progressEvent) => {
            // 处理流数据
        },
    })
}

// 取消当前请求
export function cancelQwenTalk() {
    if (currentRequestConfig) {
        const requestId = generateRequestId(currentRequestConfig);
        cancelRequest(requestId);
        currentRequestConfig = null;
    }
}

3.2 使用示例

javascript 复制代码
// 发起请求
const requestPromise = qwenTalk({message: 'Hello'}, (data) => {
    console.log('收到数据:', data);
});

// 取消请求
cancelQwenTalk();

4. 关键点总结

  1. 唯一请求标识:通过 URL、方法、参数和数据生成唯一ID,确保能准确识别请求
  2. 控制器管理:使用 Map 存储所有活动的 AbortController
  3. 自动取消:在请求拦截器中自动取消重复请求
  4. 手动取消:提供接口让开发者可以手动取消特定请求
  5. 资源清理:请求完成后及时清理控制器,避免内存泄漏
  6. 流式处理:对于流式请求,需要额外管理状态(如 buffer 和 position)

5. 最佳实践

  1. 对于耗时请求(如大文件上传/下载、流式响应)一定要实现取消功能
  2. 页面/组件卸载时取消所有未完成的请求
  3. 用户导航离开时考虑取消不必要的请求
  4. 对于表单提交等场景,可以防止重复提交

6. 注意事项

  1. 取消请求后,Axios 会抛出 CancelError,需要适当处理
  2. 确保请求ID生成逻辑覆盖所有可能影响请求唯一性的因素
  3. 在单页应用中,组件卸载时要清理相关请求
相关推荐
damoluomu5 分钟前
静态站点生成器怎么选:Node.js 生态里常用的五个(番外一)
javascript·css·typescript·node.js·html
西索斯coding44 分钟前
Claude Code 工具调用返回空怎么办?claude-opus-5.5 的 input_schema 校验踩坑记录与修复方案
服务器·前端·javascript·ai
Jlzn88881 小时前
华南PACK线自动化率怎么统计才靠谱
前端·javascript·自动化
Hilaku1 小时前
ERP选型时不问MCP,等于默认三年后再付一次集成费!
前端·javascript·程序员
三十而立洋1 小时前
前端模块化的演进:从全局变量到 Import Maps
前端·javascript·前端工程化
小凯在掘金1 小时前
一文搞懂 Map、Set、WeakMap、WeakSet
javascript·面试
用户1733598075371 小时前
纯前端 PDF 按书签拆分避坑指南:拆分后章节对不上?
前端·javascript·vue.js
AI情绪识别开源2 小时前
检信 ALLEMOTION AI驱动振动感知健康分析平台核心基础
javascript
мо仙堡杠把子ご灬2 小时前
Flex 自适应布局:告别 calc,用一个 height-auto 搞定
前端·javascript
程序员阿黄2 小时前
月圆人圆:中秋佳节传统文化站(月夜主题重设计版)项目全解析
javascript·html5·毕设