一、CancelToken 原理
CancelToken 是 Axios 提供的请求取消机制,其核心原理基于发布订阅模式和 Promise 控制:
-
发布订阅模式:CancelToken 实例通过 subscribe 方法订阅取消消息,当外部调用 cancel 方法时会触发订阅器取消请求。
-
Promise 控制:
- source 中的 token 是一个处于 pending 状态的 Promise
- cancel 方法是 token 的 Promise 对象的 resolve 触发器
- 当调用 cancel 时,Promise 状态变为 resolved,触发 then 回调执行 xhr.abort()
-
内部流程:
- 创建 CancelToken 时会传入一个 executor 函数
- executor 接收 cancel 函数作为参数
- 调用 cancel 后,Axios 会检测到并取消对应请求
二、基本用法
1. 取消单个请求
const { CancelToken } = axios;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel('Operation canceled by user');
2. 取消多个请求
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
});
// 取消所有使用该 token 的请求
source.cancel('Operation canceled by user');
三、实际应用示例
1. 防止重复请求
let pendingRequest = null;
function fetchData() {
// 如果已有请求在进行,先取消
if (pendingRequest) {
pendingRequest('Cancel previous request');
}
const source = CancelToken.source();
pendingRequest = source.cancel;
return axios.get('/api/data', {
cancelToken: source.token
}).finally(() => {
pendingRequest = null;
});
}
2. 路由切换时取消请求
// Vue 路由守卫示例
router.beforeEach((to, from, next) => {
// 取消所有 pending 的请求
if (window.axiosPendingRequests) {
window.axiosPendingRequests.forEach(request => {
request.cancel('Navigation canceled');
});
window.axiosPendingRequests = [];
}
next();
});
// 封装请求时记录
window.axiosPendingRequests = [];
const source = CancelToken.source();
window.axiosPendingRequests.push(source);
axios.get('/api/data', {
cancelToken: source.token
}).then(response => {
// 请求完成后从数组中移除
const index = window.axiosPendingRequests.indexOf(source);
if (index > -1) {
window.axiosPendingRequests.splice(index, 1);
}
});
四、错误处理
取消请求会触发错误回调,需要通过 axios.isCancel
判断是否为取消操作:
axios.get('/user/12345', {
cancelToken: source.token
}).catch(thrown => {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理其他错误
console.error(thrown);
}
});
五、注意事项
- 取消请求后,Promise 会进入 rejected 状态,需要正确处理
- 在组件卸载或路由跳转时,建议取消所有 pending 请求
- 对于频繁触发的请求(如搜索建议),使用 CancelToken 可以有效避免竞态问题