axios全局封装AbortController取消重复请求

为什么?

问题:为什么axios要配置AbortController?防抖节流不行吗?

分析:

  • 防抖节流本质上是用延时器来操作请求的。
  • 防抖是判断延时器是否存在,如果存在,清除延时器,重新开启一个延时器,只执行最后一次请求。
  • 节流呢,是判断延时器是否存在,如果存在,直接return掉,直到执行完这个延时器。
  • 事实上,这些体验感都不算友好,因为对于用户来说,得等一些时间,尤其是首次请求,不是那么流畅。
  • 而当我们配置了AbortController,就可以中断掉重复的请求,并且首次不需要等待时间,是非常友好的行为。
  • 从axios0.22.0开始,开始支持AbortController,并且废弃CancelToken。之所以我们不用CancelToken来取消请求,是因为CancelToken存在内存泄漏隐患,并不安全,所以官方人员希望用户们升级版本,使用AbortController减少内存泄漏风险。
  • 事实上,凡事用xhr封装的请求库,都可以配置AbortController,如fetch,axios,alova等等
  • 本文封装的原理可以移步至另一篇博客查看:如何避免接口重复请求(axios推荐使用AbortController)-CSDN博客

前置条件:

升级axios至最新版本。因为AbortController是从0.22.0开始支持的。如果你的版本已经在0.22.0以上,可以正常使用,不升级亦可。

以笔者升级的项目为例,我这里的版本是很古老的0.18.0,所以我这里是直接把axios升级至目前最新的稳定版本1.7.2。(如果升级失败,可以先移除掉axios,再重新安装。)

javascript 复制代码
//安装axios
npm i axios
//卸载axios
npm uninstall axios

接入步骤:

1.在封装axios的全局文件中,先定义两个变量

复制代码
在全局封装axios的request.js文件的全局里定义两个变量
isCancel 用来判断请求是否被取消
cacheRequest 用来存储需要取消重复请求的接口
javascript 复制代码
// isCancel-取消标识 可以根据这个值判断请求是否被取消
const { isCancel } = axios
const cacheRequest = {}

2.定义一个函数,用来取消请求和删除cacheRequest里对应的请求

javascript 复制代码
// 删除缓存队列中的请求
function abortCacheRequest(reqKey) {
  if (cacheRequest[reqKey]) {
    // 通过AbortController实例上的abort来终止请求
    cacheRequest[reqKey].abort()
    delete cacheRequest[reqKey]
  }
}

3.在请求拦截器里加入取消重复请求的逻辑

之所以做成根据isAbort标识判断是否使用AbortController,是因为并不是所有的请求都是需要取消重复请求的,就像并不是所有的请求都需要防抖节流一样,所以当我们有需要的时候,再加这个标识就好。

javascript 复制代码
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
    }
})

4.在响应拦截器里加入判断

如果请求成功后,清除cacheRequest里对应的存储;并且在error里判断,取消的请求不做任何处理

javascript 复制代码
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: '重复请求,已取消'
    })
  }
}

5.使用

在调用接口的时候,增加一个isAbort的标识为true就能开启取消重复请求的功能了

javascript 复制代码
//请求示例
export function getList(data) {
  return request({
    url: '/list',
    method: 'post',
    data: data,
    isAbort: true // 配置标识 如果该接口频繁请求 则会中断上次请求保留最新一次请求
  })
}

效果图:

体验感极佳,status为canceled,都是被取消的请求

小拓展:

如何取消xhr的请求:

javascript 复制代码
xhr.abort()

如何取消fetch请求:

javascript 复制代码
let controller = new AbortController();
let signal = controller.signal;

fetch(url, {signal});

controller.abort(); // 取消请求

//监听请求取消情况
signal.addEventListener('abort',
  () => console.log('abort!',signal.aborted)
);
相关推荐
musk12129 分钟前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘38 分钟前
js代码09
开发语言·javascript·ecmascript
万少1 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL1 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl021 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿2 小时前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端
百锦再2 小时前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref