axios取消上一个同名同类型/上一页面所有未响应请求

95 阅读2分钟

在日常牛马的时候,难免会有场景需要取消上一次同名同类型请求或上一页面的所有请求,防止页面渲染问题,这里记录一下,有什么建议或意见,希望各位读者老爷们提一提!

项目首先引入Axios、vue-router,这里就不多赘述了

  1. 类型文件 type.ts,请求接口的时候增加一个拒绝加入取消请求集合的参数
import { AxiosRequestConfig } from 'axios'

export interface CustomConfig extends AxiosRequestConfig {
  isNotEnterCancelMap?: Boolean
}

  1. 新建ts文件,cancelRequest.ts
import type { CustomConfig } from './type'

// 取消请求映射集合
const pendingRequests = new Map()
// 白名单
const whiteList = new Set()

/**
 * @description: 生成取消请求的key
 * @param {CustomConfig} config
 * @return {*}
 */
const generateRequestKey = (config: CustomConfig) => {
  const { url, method } = config
  return [method, url].join('&')
}

/**
 * @description: 添加白名单项
 * @param {CustomConfig} config
 * @return {*}
 */
export const setWhiteListKey = (config: CustomConfig) => {
  const whiteItem = generateRequestKey(config)
  whiteList.add(whiteItem)
}

/**
 * @description: 缓存pending状态下的请求
 * @param {CustomConfig} config
 * @return {*}
 */
export const addPendingRequest = (config: CustomConfig) => {
  const requestKey = generateRequestKey(config)
  if (whiteList.has(requestKey)) {
    return
  }
  if (pendingRequests.has(requestKey)) {
    // 取消请求
    pendingRequests.get(requestKey).abort()
    pendingRequests.delete(requestKey)
  }
  const controller = new AbortController()
  config.signal = controller.signal
  pendingRequests.set(requestKey, controller)
}

/**
 * @description: 删除已成功响应的请求
 * @param {CustomConfig} config
 * @return {*}
 */
export const removePendingRequest = (config: CustomConfig) => {
  const requestKey = generateRequestKey(config)
  if (pendingRequests.has(requestKey)) {
    pendingRequests.delete(requestKey)
  }
}

/**
 * @description: 取消所有未响应请求
 * @return {*}
 */
export const clearPendingRequests = () => {
  pendingRequests.forEach((val, key) => {
    val.abort()
  })
  pendingRequests.clear()
}

  1. 取消上一次同名请求,在请求拦截器中调用取消函数
...
import { addPendingRequest, removePendingRequest, setWhiteListKey } from './cancelRequest'
import type { CustomConfig } from './type'

...

const setInterceptors = (instance: AxiosInstance) => {
  instance.interceptors.request.use(
    config => {
    // 加入集合
      addPendingRequest(config)
      return config
    },
    error => {
      console.log('error', error)
    }
  )
  instance.interceptors.response.use(
    result => {
    // 取消上一个同名请求
      removePendingRequest(result.config)
      return result.data
    },
    error => {
      return Promise.reject(error)
    }
  )
}

class Http {
  // 创建axios实例
  protected axiosInstance: AxiosInstance

  constructor(config: AxiosRequestConfig) {
    this.axiosInstance = Axios.create(config)
    setInterceptors(this.axiosInstance)
  }

  request<T>(config: CustomConfig): Promise<T> {
  // 判断是否加入白名单
    config.isNotEnterCancelMap && setWhiteListKey(config)
    return new Promise(resolve => {
      this.axiosInstance.request<any, T>(config).then(res => {
        resolve(res)
      })
    })
  }

  get<T>(config: CustomConfig): Promise<T> {
    return this.request({ ...config, method: 'get' })
  }
  ...
}

export const http = new Http(createBasicConfig())

  1. 取消上一页面的请求,在路由前置守卫中调用clearPendingRequests函数,取消集合中所有未响应的请求
...
import { clearPendingRequests } from '../utils/cancelRequest'

...
router.beforeEach((to, from, next) => {
  clearPendingRequests()
  next()
})

5、请求示例

 const result = await http.get({
    url: '/user/list',
    params: {
      page: page.value
    },
    // isNotEnterCancelMap: false
  })
  ...