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. 在单页应用中,组件卸载时要清理相关请求
相关推荐
跟着珅聪学java5 小时前
js编写中文转unicode 教程
前端·javascript·数据库
英俊潇洒美少年5 小时前
Vue3 深入响应式系统
前端·javascript·vue.js
颜酱5 小时前
回溯算法实战练习(3)
javascript·后端·算法
我命由我123456 小时前
React Router 6 - 概述、基础路由、重定向、NavLink、路由表
前端·javascript·react.js·前端框架·ecmascript·html5·js
yaaakaaang7 小时前
(四)前端,如此简单!---Promise
前端·javascript
aini_lovee7 小时前
C# 实现邮件发送源码(支持附件)
开发语言·javascript·c#
英俊潇洒美少年8 小时前
js 进程与线程的讲解
javascript
汉堡大王95279 小时前
# AI 终于能"干活"了——Function Calling 完全指南
javascript·人工智能·机器学习
吴声子夜歌9 小时前
JavaScript——call()、apply()和bind()
开发语言·前端·javascript
小哈猪9 小时前
CSS Flex 与 Grid:谁才是布局之王?
javascript