axios-CancelToken、AbortController取消重复请求

场景

在开发中会遇到连续点击按钮同时多次请求相同接口,一般我们会对按钮进行一个loading操作,但是有时候可能是一个tab页签,点击页签就要请求一次数据,当频繁的来回点击页签的时候,会发送多次请求,如果在数据还没有请求到的时候禁用tab页签点击,会给用户一种页签组件点不动的感觉,于是我考虑换个思路,发现本次的请求和之前的请求相同,则取消之前的请求,以本次的请求为准。

V1.0(使用CancelToken,无白名单,基本够用)

由于项目中使用到的是axios来进行请求,经过一通百度大法,发现axios有一个叫CancelToken的api,用于给请求打上一个标识,并返回一个函数,只要调用这个函数,就可以取消被标识的请求

整体思路如下:

1.定义请求队列,以唯一标识(url+method)为key来缓存取消函数

2.在请求拦截中判断,如果标识相同,说明为重复请求,根据唯一标识删除缓存队列中的取消函数,再将新的取消函数进行缓存

3.响应拦截中判断,如果本次请求成功,根据唯一标识删除缓存队列中的取消函数,如果请求失败,通过axios内置函数isCancel判断,本次失败的原因是接口请求失败,还是通过CancelToken进行的取消

import axios from 'axios'

// CancelToken能为一次请求‘打标识’
// isCancel用于判断请求是不是被CancelToken取消的
const { CancelToken, isCancel } = axios

// 请求队列,缓存发出的请求
const cacheRequest = {}

// axios实例
const service = axios.create({
  baseURL: '/api',
  headers: {
    'Content-Type': 'application/json; charset=UTF-8'
  },
})

// 请求拦截
service.interceptors.request.use((config) => {
    const { url, method } = config
    // 请求地址和请求方式组成唯一标识,将这个标识作为取消函数的key,保存到请求队列中
    const reqKey = `${url}&${method}`
    // 如果存在重复请求,删除之前的请求
    removeCacheRequest(reqKey)
    // 将请求加入请求队列
    config.cancelToken = new CancelToken(c => {
      cacheRequest[reqKey] = c
    })
})

// 响应拦截
service.interceptors.response.use(
    (response) => {
        // 请求成功,从队列中移除
        const { url, method } = response.config
        removeCacheRequest(`${url}&${method}`)
    },
    (error) => {
        // 请求失败,使用isCancel来区分是被CancelToken取消,还是常规的请求失败
        if (isCancel(error)) {
          // 通过CancelToken取消的请求不做任何处理
          return Promise.reject({
            message: '重复请求,自动拦截并取消'
          })
        } else{
            // 正常请求发生错误,抛出异常等统一提示
            console.log(error.response, 'errMsg')
        }
    }
)

/**
 * @desc 删除缓存队列中的请求
 * @param {String} reqKey 本次请求的唯一标识 url&method
 */
function removeCacheRequest(reqKey) {
  if (cacheRequest[reqKey]) {
    // 这里调用的就是上面的CancelToken生成的c函数,调用就会取消请求
    cacheRequest[reqKey]()
    delete cacheRequest[reqKey]
  }
}

看起来没有问题了,确实也达到了我所想要的效果,之前的请求如果没有完成都被取消,始终只有最新的请求会被发出去

 V2.0(使用AbortController,增加白名单)

回想自己用了这么久axios,都还不知道官方提供了这种api,于是我决定翻一下文档,复习一下axios,结果一翻不得了,官方标识从v0.22.0开始已经弃用CancelToken,建议我们使用AbortController来实现,由于项目中使用的是0.26.1(明明还是可以用),还是紧跟时代步伐,换成官方推荐的吧。

 将上面的代码进行改造,顺便加了一个白名单,因为项目是vue的后台项目,有可能几个兄弟组件在初始化的时候都请求了同一个接口,这种情况下应该允许这些接口的重复请求

import axios from 'axios'

// 注意:AbortController是fetch API提供的,不需要从axios引入
// isCancel用于判断请求是不是被AbortController取消的
const { isCancel } = axios

// 请求队列,缓存发出的请求
const cacheRequest = {}
// 不进行重复请求拦截的白名单
const cacheWhiteList = ['/foo/bar&get']

// axios实例
const service = axios.create({
  baseURL: '/api',
  headers: {
    'Content-Type': 'application/json; charset=UTF-8'
  },
})

// 请求拦截
service.interceptors.request.use((config) => {
    const { url, method } = config
    // 请求地址和请求方式组成唯一标识,将这个标识作为取消函数的key,保存到请求队列中
    const reqKey = `${url}&${method}`
    // 如果存在重复请求,删除之前的请求
    if (cacheWhiteList.indexOf(reqKey) === -1) {
      removeCacheRequest(reqKey)
      // 将请求加入请求队列,通过AbortController来进行手动取消
      const controller = new AbortController()
      config.signal = controller.signal
      cacheRequest[reqKey] = controller
    }
})

// 响应拦截
service.interceptors.response.use(
    (response) => {
        // 请求成功,从队列中移除
        const { url, method } = response.config
        removeCacheRequest(`${url}&${method}`)
    },
    (error) => {
        // 请求失败,使用isCancel来区分是被CancelToken取消,还是常规的请求失败
        if (isCancel(error)) {
          // 通过CancelToken取消的请求不做任何处理
          return Promise.reject({
            message: '重复请求,自动拦截并取消'
          })
        } else{
            // 正常请求发生错误,抛出异常等统一提示
            console.log(error.response, 'errMsg')
        }
    }
)

/**
 * @desc 删除缓存队列中的请求
 * @param {String} reqKey 本次请求的唯一标识 url&method
 */
function removeCacheRequest(reqKey) {
  if (cacheRequest[reqKey]) {
    // 通过AbortController实例上的abort来进行请求的取消
    cacheRequest[reqKey].abort()
    delete cacheRequest[reqKey]
  }
}

在查阅资料的过程中大部分教程都是使用的CancelToken按照项目的目前情况也够用,但是发现没有写的特别易懂的使用AbortController方式来取消的教程,所以这里也记录一下

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值