问题:为什么axios要配置AbortController?防抖节流不行吗?
分析:
升级axios至最新版本。因为AbortController是从0.22.0开始支持的。如果你的版本已经在0.22.0以上,可以正常使用,不升级亦可。
以笔者升级的项目为例,我这里的版本是很古老的0.18.0,所以我这里是直接把axios升级至目前最新的稳定版本1.7.2。(如果升级失败,可以先移除掉axios,再重新安装。)
//安装axios
npm i axios
//卸载axios
npm uninstall axios
在全局封装axios的request.js文件的全局里定义两个变量 isCancel 用来判断请求是否被取消 cacheRequest 用来存储需要取消重复请求的接口
// isCancel-取消标识 可以根据这个值判断请求是否被取消
const { isCancel } = axios
const cacheRequest = {}
// 删除缓存队列中的请求
function abortCacheRequest(reqKey) {
if (cacheRequest[reqKey]) {
// 通过AbortController实例上的abort来终止请求
cacheRequest[reqKey].abort()
delete cacheRequest[reqKey]
}
}
之所以做成根据isAbort标识判断是否使用AbortController,是因为并不是所有的请求都是需要取消重复请求的,就像并不是所有的请求都需要防抖节流一样,所以当我们有需要的时候,再加这个标识就好。
const service = axios.create({
baseURL: hosts.server, // api 的 base_url
timeout: 50e3, // request timeout
});
service.interceptors.request.use(config => {
// isAbort - 是config里配置的是否清除相同请求的标识,不传则默认是不需要清除
const { url, method, isAbort= false } = config
if (isAbort) {
// 请求地址和请求方式组成唯一标识,将这个标识作为取消函数的key,保存到请求队列中
const reqKey = `${url}&${method}`
// 如果config传了需要清除重复请求的isAbort,则如果存在重复请求,删除之前的请求
abortCacheRequest(reqKey)
// 将请求加入请求队列,通过AbortController来进行手动取消
const controller = new AbortController()
config.signal = controller.signal
cacheRequest[reqKey] = controller
}
})
如果请求成功后,清除cacheRequest里对应的存储;并且在error里判断,取消的请求不做任何处理
service.interceptors.response.use(res => {
// 请求成功,从队列中移除
const { url, method, isAbort = false } = response.config
if (isAbort) delete cacheRequest[`${url}&${method}`]
}), error => {
if (isCancel(error)) {
// 通过AbortController取消的请求不做任何处理
return Promise.reject({
message: '重复请求,已取消'
})
}
}
在调用接口的时候,增加一个isAbort的标识为true就能开启取消重复请求的功能了
//请求示例
export function getList(data) {
return request({
url: '/list',
method: 'post',
data: data,
isAbort: true // 配置标识 如果该接口频繁请求 则会中断上次请求保留最新一次请求
})
}
体验感极佳,status为canceled,都是被取消的请求
如何取消xhr的请求:
xhr.abort()
如何取消fetch请求:
let controller = new AbortController();
let signal = controller.signal;
fetch(url, {signal});
controller.abort(); // 取消请求
//监听请求取消情况
signal.addEventListener('abort',
() => console.log('abort!',signal.aborted)
);