Axios 取消请求
'# Axios 取消请求
一、背景与问题
在现代 Web 应用开发中,频繁的 API 请求是常态。但当用户快速切换操作时,可能会产生大量未完成的请求,导致资源浪费和性能下降。例如,用户在搜索框中连续输入多个关键词时,前一次的请求可能还未完成,此时若不主动取消,就会产生冗余请求。
Axios 提供了取消请求的机制,通过 CancelToken 和 AbortController 实现请求的主动中断。但其原理和使用场景需要深入理解,本文将从底层机制到实际应用进行深度解析。
二、基本原理
Axios 的取消请求机制基于浏览器原生的 AbortController API,通过以下步骤实现请求中断:
- 创建
AbortController实例 - 通过
signal属性将取消控制权传递给请求 - 在需要取消时调用
abort()方法 - 服务器端通过
Connection: close头终止连接(HTTP/1.1)
关键点:
- HTTP 协议本身不支持取消请求,Axios 通过
AbortController在客户端主动终止 TCP 连接 - 服务器端可能不会响应中断请求(需具体实现支持)
- 请求取消后,会触发
AbortError异常
三、环境准备
确保项目中已安装 Axios:
npm install axios四、核心实现
1. 基础用法(推荐方式)
// 创建取消控制器
const controller = new AbortController();
// 发起请求
axios.get('/api/data', {
signal: controller.signal
})
.then(response => {
console.log('成功:', response.data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('请求已取消');
} else {
console.error('请求失败:', error.message);
}
});
// 3秒后取消请求
setTimeout(() => {
controller.abort();
}, 3000);关键代码解释:
AbortController创建信号控制对象signal属性绑定到请求配置controller.abort()发送中断信号AbortError区分普通错误和取消错误
2. 使用 CancelToken(旧版方式)
const source = axios.CancelToken.source();
axios.get('/api/data', {
cancelToken: source.token
})
.then(response => {
console.log('成功:', response.data);
})
.catch(error => {
if (axios.isCancel(error)) {
console.log('请求已取消');
} else {
console.error('请求失败:', error.message);
}
});
// 3秒后取消请求
setTimeout(() => {
source.cancel('用户主动取消请求');
}, 3000);关键区别:
CancelToken需要手动管理取消源AbortController是原生 API,支持更细粒度控制AbortError是原生错误类型,而CancelToken会抛出Cancel类型的错误
3. 带超时的取消策略
const controller = new AbortController();
axios.get('/api/data', {
signal: controller.signal,
timeout: 5000 // 5秒超时
})
.then(response => {
console.log('成功:', response.data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('请求已取消或超时');
} else {
console.error('请求失败:', error.message);
}
});
// 3秒后取消请求
setTimeout(() => {
controller.abort();
}, 3000);性能优化点:
- 超时机制可避免等待无效响应
- 结合取消策略可避免资源浪费
- 需注意超时时间与取消间隔的合理设置
五、完整案例
React 组件示例:搜索请求取消
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function SearchComponent() {
const [results, setResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
let controller = new AbortController();
const fetchData = async () => {
setIsLoading(true);
try {
const response = await axios.get('/api/search', {
signal: controller.signal,
params: { query: 'test' }
});
setResults(response.data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('请求已取消');
} else {
console.error('请求失败:', error.message);
}
} finally {
setIsLoading(false);
}
};
fetchData();
// 清理函数:组件卸载时取消请求
return () => {
controller.abort();
};
}, []);
return (
<div>
<h2>搜索结果</h2>
{isLoading && <p>加载中...</p>}
<ul>
{results.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}关键点:
- 使用
useEffect管理请求生命周期 - 在清理函数中主动取消请求
- 处理 AbortError 与普通错误的区别
- 避免组件卸载时遗留未完成请求
六、源码解析
Axios 的取消机制在 src/axios.js 中实现,关键代码如下:
function createInstance() {
const instance = axios.create();
// 重写 request 方法
instance.request = function(config) {
const signal = config.signal;
if (signal && signal.aborted) {
throw new axios.Cancel('Request aborted');
}
// 原生 AbortController 实现
if (signal instanceof AbortController) {
signal.signal.addEventListener('abort', () => {
config.onDownloadProgress?.({ aborted: true });
config.onUploadProgress?.({ aborted: true });
});
}
return instance._request(config);
};
return instance;
}关键机制:
- 检查 signal 是否被中止
- 监听 abort 事件触发进度回调
- 在底层使用
XMLHttpRequest或fetch时处理中断
七、进阶使用
1. 多请求取消管理
const controller = new AbortController();
// 三个并发请求
axios.get('/api/data1', { signal: controller.signal })
.catch(() => {});
axios.get('/api/data2', { signal: controller.signal })
.catch(() => {});
axios.get('/api/data3', { signal: controller.signal })
.catch(() => {});
// 5秒后取消所有请求
setTimeout(() => {
controller.abort();
}, 5000);2. 结合 Vue 路由守卫
router.beforeEach((to, from, next) => {
// 取消当前页面的请求
if (window.cancelToken) {
window.cancelToken.abort();
}
next();
});3. 自定义取消管理器
class CancelManager {
constructor() {
this.controllers = new Map();
}
createToken() {
const controller = new AbortController();
this.controllers.set(Date.now(), controller);
return controller.signal;
}
cancelAll() {
this.controllers.forEach(controller => {
controller.abort();
});
}
}八、性能与工程实践
1. 性能优化策略
- 批量取消:避免频繁创建 AbortController 实例
- 缓存信号:复用已有 controller 实例
- 超时控制:设置合理的请求超时时间
- 资源清理:在组件卸载或路由切换时主动取消
2. 异常处理规范
try {
await axios.get('/api/data', { signal: controller.signal });
} catch (error) {
if (error.name === 'AbortError') {
console.log('请求已取消');
} else if (error.response) {
console.error('服务器响应错误:', error.response.status);
} else {
console.error('网络错误:', error.message);
}
}3. 安全注意事项
- 避免敏感数据泄露:取消请求时注意清理缓存
- 防止滥用:限制取消请求的频率
- 服务器端支持:确保后端能正确处理中断请求
九、常见问题与踩坑
1. 常见错误
错误示例:
const controller = new AbortController();
axios.get('/api/data', { signal: controller.signal });
controller.abort(); // 顺序错误问题分析:在发送请求前就调用 abort() 会导致请求未发送就被取消
解决方案:确保在请求发送后才调用 abort()
2. 跨域问题
错误场景:某些服务器不支持 abort 请求,可能导致无法取消
解决方案:增加 Connection: close 头,强制服务器关闭连接
3. 记忆泄漏
错误场景:未正确清理控制器导致内存占用
解决方案:在组件卸载或路由切换时主动调用 abort()
十、最佳实践
1. 推荐方案
- 使用
AbortController替代旧版 CancelToken - 在用户输入时取消之前的搜索请求
- 在组件卸载时清理所有未完成的请求
- 在 API 调用前检查是否需要取消
2. 使用场景
- 用户快速点击按钮时
- 页面导航时
- 输入框输入时
- 超时未响应时
3. 不推荐场景
- 必须完成的支付确认
- 上传文件时(可能丢失数据)
- 需要服务器端确认的业务操作
十一、总结
Axios 的取消请求机制是现代 Web 开发中避免资源浪费的重要手段。通过 AbortController 实现的主动中断,结合合理的使用场景,可以显著提升应用性能和用户体验。但在使用过程中需注意:避免在未发送请求时调用 abort(),确保服务器端支持中断操作,以及在组件卸载时进行清理。
实际开发中,建议结合具体业务场景选择适合的取消策略,例如在输入框中使用节流+取消,或在导航时使用全局取消管理。同时,注意处理各种异常情况,确保应用的健壮性。通过合理使用取消请求,可以有效提升应用的响应速度和资源利用率。
评论已关闭