从 v0.22.0
开始,Axios 支持以 fetch API 方式—— AbortController 取消请求
此 API 从 v0.22.0
开始已被弃用,不应在新项目中使用
官网链接
1. 背景
最近项目中遇到一个场景,当连续触发一个请求时,如果是同一个接口,则保留最后一次的请求,之前的请求取消。
查阅了下axios文档,有一个属性CancelToken,把这个添加到axios配置中
2. 使用
1:在request时,添加cancelToken
request: [
(config: AxiosRequestConfig) => {
const cacheKey = `${config.method}${config.url}`
if (config.autoCancel) {
removeCache(cacheKey)
}
config.cancelToken = new axios.CancelToken((c) => {
caches[cacheKey] = c
})
return config
},
(error: any) => Promise.reject(error),
],
2:在reponse时,删除key
response: [
(res: AxiosResponse) => {
const cacheKey = `${res.config.method}${res.config.url}`
if (res.config.autoCancel) {
removeCache(cacheKey)
}
return res
},
(error: any) => Promise.reject(error),
],
3:判断是否存在重复请求
const caches: Record<string, Canceler> = {}
function removeCache(key: string) {
if (caches[key]) {
caches[key]()
delete caches[key]
}
}
这里的autoCancel是为了解决url相同,请求参数不同时,自定义添加的,具体请求方式可以根据这个值来决定是否开启cancelToken
3. 全部代码
/**
* 通过取消重复请求解决请求“竞态”问题
* - 如何定义“重复”:method和url相同
*/
import axios, { AxiosRequestConfig, Canceler, AxiosResponse } from 'axios'
const caches: Record<string, Canceler> = {}
function removeCache(key: string) {
if (caches[key]) {
caches[key]()
delete caches[key]
}
}
const cancelInterceptors = {
request: [
(config: AxiosRequestConfig) => {
const cacheKey = `${config.method}${config.url}`
if (config.autoCancel) {
removeCache(cacheKey)
}
config.cancelToken = new axios.CancelToken((c) => {
caches[cacheKey] = c
})
return config
},
(error: any) => Promise.reject(error),
],
response: [
(res: AxiosResponse) => {
const cacheKey = `${res.config.method}${res.config.url}`
if (res.config.autoCancel) {
removeCache(cacheKey)
}
return res
},
(error: any) => Promise.reject(error),
],
}
export default cancelInterceptors
在封装的axios里面添加配置
// 往request请求中添加配置
service.interceptors.request.use(...cancelInterceptors.request)
// 往response请求中添加配置
service.interceptors.response.use(...cancelInterceptors.response)
在response失败error中axios返回了一个失败状态axios.isCancel(error)
(error: AxiosError) => {
// if (axios.isCancel(error) && error.message === SCRM_CANCEL_MESSAGE) {
// // 被手动取消的数据统计接口,不展示提示
if (axios.isCancel(error)) {
// 被取消的接口,不展示提示
} else {
message.error(error.response?.statusText || error.message || '网络错误')
}
// 网络层面错误,如接口地址写错了会走到这里
return Promise.reject(error)
}
3. 原理
source file:axios/lib/adapters/xhr.js
1:创建请求
var request = new XMLHttpRequest()
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
2:创建新的取消
在source file:axios/lib/cancel/CancelToken.js
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
2:取消请求
在axios/lib/adapters/xhr.js文件中取消request.abort();请求,
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
笔记