🌐 Axios 请求取消机制完全教程(含重复请求处理)
✨ 适合 Vue / React 项目 Axios 封装使用,含原理讲解、实战演示与最佳实践。
📌 记得收藏、点赞、关注,持续复习不迷路!
📖 目录
🧠 前言
在现代前端中,同一个接口短时间被多次调用可能会导致:
- 后端压力加剧
- 前端响应错乱
- 表单重复提交
- 用户体验下降
Axios 提供了 CancelToken
机制来主动取消 HTTP 请求。通过配合 Map
管理 cancel 函数,可以实现在请求发起前自动取消上一次相同请求。
🎯 核心目标
我们希望实现以下功能:
- 同一接口重复请求时自动取消前一个
- 请求完成后自动清理内存
- 能判断错误是否由取消引起
- 可复用的 Axios 封装逻辑
🧩 请求唯一标识 key 生成
function getRequestKey(config: AxiosRequestConfig): string {
const { method, url, params, data } = config
return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}
用于为每个请求生成唯一字符串表示,作为 Map
的 key,防止重复。
🧩 Axios 取消请求的三种角色
1. cancelToken: CancelToken
这是挂在请求 config
上的控制对象,Axios 内部会监听它的状态来判断是否中止请求。
2. cancel: (message) => void
你主动调用这个函数,就会触发取消机制,Axios 自动终止请求。
3. axios.isCancel(error)
这是用于在 catch
中判断请求是否是**“被取消”导致的失败**。
axios.isCancel(error) // true 表示是取消请求导致的错误
🔍 CancelToken 原理详解
✅ 方式一:底层写法
config.cancelToken = new axios.CancelToken((cancel) => {
pendingRequestMap.set(requestKey, cancel)
})
CancelToken
接收一个执行器函数,提供cancel
参数。- 你把
cancel
函数保存进 Map,后面可以调用来取消。
✅ 方式二:推荐写法(语法糖)
const source = axios.CancelToken.source()
config.cancelToken = source.token
pendingRequestMap.set(requestKey, source.cancel)
source.token
:绑定到请求,Axios 内部监听source.cancel()
:主动触发取消
💡 可选链 ?.
函数调用解读
pendingRequestMap.get(requestKey)?.('取消重复请求')
等价于:
const cancel = pendingRequestMap.get(requestKey)
if (cancel) cancel('取消重复请求')
作用:
- 如果 cancel 存在,就调用它
- 如果为 undefined,不报错、直接跳过
🧪 完整封装示例
import axios, { AxiosRequestConfig, Canceler } from 'axios'
const pendingRequestMap = new Map<string, Canceler>()
function getRequestKey(config: AxiosRequestConfig): string {
return [config.method, config.url, JSON.stringify(config.params), JSON.stringify(config.data)].join('&')
}
function addPendingRequest(config: AxiosRequestConfig) {
const requestKey = getRequestKey(config)
if (pendingRequestMap.has(requestKey)) {
pendingRequestMap.get(requestKey)?.('取消重复请求')
}
const source = axios.CancelToken.source()
config.cancelToken = source.token
pendingRequestMap.set(requestKey, source.cancel)
}
function removePendingRequest(config: AxiosRequestConfig) {
const requestKey = getRequestKey(config)
pendingRequestMap.delete(requestKey)
}
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000,
headers: { 'Content-Type': 'application/json' },
})
service.interceptors.request.use((config) => {
removePendingRequest(config)
addPendingRequest(config)
return config
})
service.interceptors.response.use(
(response) => {
removePendingRequest(response.config)
return response.data
},
(error) => {
if (axios.isCancel(error)) {
console.warn('请求被取消:', error.message)
return Promise.reject({ canceled: true })
}
return Promise.reject(error)
}
)
export default service
❓ 常见问题解答
✅ Q1:cancelToken
没看到我用它,它有什么用?
虽然你没主动用它,但Axios 内部监听了它的
promise
,一旦你调用了 cancel,它就会中止请求。
✅ Q2:pendingRequestMap
会不会越来越大?
不会。每次请求完成后,拦截器都会调用
removePendingRequest
,及时移除对应 key,防止内存泄漏。
✅ Q3:被取消的是“发出去的请求”吗?后端会知道吗?
- 是的,是已经发出的请求(浏览器层面被中断)。
- 后端收到请求就已经开始处理,不一定能中断逻辑,除非后端专门监听连接断开(一般不会)。
✅ Q4:axios.CancelToken.source()
和 new CancelToken(...)
区别?
方式 | 特点 |
---|---|
CancelToken.source() | ✅ 简洁封装了 token + cancel |
new CancelToken(...) | ✅ 灵活但略繁琐 |
建议用 source()
,更易读。
✅ Q5:为什么是 ?.('取消重复请求')
这么奇怪的语法?
?.(...)
是可选链函数调用写法,防止undefined()
报错。更安全。
🏁 总结
通过 Axios 的 CancelToken + pendingRequestMap,我们实现了:
✅ 重复请求自动取消
✅ 请求完成后自动清理内存
✅ 异常中判断是否是取消请求
✅ 封装逻辑易复用、可维护
📌 附:可搭配使用场景建议
场景 | 是否建议取消前一个请求 |
---|---|
搜索建议输入框 | ✅ 是,避免请求风暴 |
点击切换页面 | ✅ 是,避免残留请求 |
并发上传多个文件 | ❌ 否,每个请求应独立 |
表单重复提交 | ✅ 是,防止多次创建 |