vue3 axios异步并发请求

564 阅读3分钟

一、axios异步并发 => 取消重复请求,只保留最后一次请求

1. AbortController (推荐)

从 v0.22.0 开始,Axios 支持以 fetch API 方式—— AbortController 取消请求

示例:封装接口请求index.js

1.1 在axios中声明数组记录标识

//axios异步并发1
let pending = [] //声明一个数组用于存储每个请求的标识
//AbortController取消请求1
let controller

let removePending = (config) => {
  //查找数组中是否存在相同请求,存在则取消
  if (pending && pending.length > 0) {
    for (let p in pending) {
      if (pending[p].u === config.url.split('?')[0] + '&' + config.method) {
        if(pending[p].f){
          pending[p].f() //执行取消操作
        }
        //axios AbortController取消请求2
        controller.abort()
        pending.splice(p, 1) //数组移除当前请求
      }
    }
  }
}

1.2 在请求前校验拦截

class Request {
  constructor(baseURL, timeout = 10000) {
    this.instance = axios.create({
      baseURL: 'https://juejin.cn/creator/content/column?status=all',
      timeout,
      headers: {
        'Content-Type': 'application/json'
      }
    })
    
    this.instance.interceptors.request.use(
      (config) => {
      
        //axios异步并发2
        removePending(config) //在一个axios发送前执行校验取消操作
        
        //axios AbortController取消请求3
        pending.push({
          u:
              config.url.split('?')[0] +
              '&' +
              config.method
        })
        controller = new AbortController()
        config.signal = controller.signal
        config.controller = controller
        
        return config
      },
      (err) => {
        return err
      }
    )
    
  }
}

1.3 在请求返回后维护声明数组pending

class Request {
  constructor(baseURL, timeout = 10000) {
    this.instance = axios.create({
      baseURL: 'https://juejin.cn/creator/content/column?status=all',
      timeout,
      headers: {
        'Content-Type': 'application/json'
      }
    })
    
    this.instance.interceptors.response.use(
      (res) => {
      
        //axios异步并发3
        // 清除当前记录
        if (pending && pending.length > 0) {
          for (let p in pending) {
            if(pending[p].u === res.config.url.split('?')[0] + '&' + res.config.method) {
              pending.splice(p, 1)
            }
          }
        }
        
        useLoadingData.showLoading = false
        return res
      },
      (err) => {
        //axios异步并发4
        pending = [] //清空记录

        // console.log("err", err)
        useLoadingData.showLoading = false
        // // return err
        return Promise.reject(err)
      }
    )
    
  }
}

完整示例:

const TIMEOUT = 10000

//axios异步并发1
let pending = [] //声明一个数组用于存储每个请求的标识
//AbortController取消请求1
let controller

let removePending = (config) => {
  //查找数组中是否存在相同请求,存在则取消
  if (pending && pending.length > 0) {
    for (let p in pending) {
      if (pending[p].u === config.url.split('?')[0] + '&' + config.method) {
        if(pending[p].f){
          pending[p].f() //执行取消操作
        }
        //axios AbortController取消请求2
        controller.abort()
        pending.splice(p, 1) //数组移除当前请求
      }
    }
  }
}

class Request {
  constructor(baseURL, timeout = 10000) {
    this.instance = axios.create({
      baseURL: 'https://juejin.cn/creator/content/column?status=all',
      timeout,
      headers: {
        'Content-Type': 'application/json'
      }
    })
    
    this.instance.interceptors.request.use(
      (config) => {
      
        //axios异步并发2
        removePending(config) //在一个axios发送前执行校验取消操作
        
        //axios AbortController取消请求3
        pending.push({
          u:
              config.url.split('?')[0] +
              '&' +
              config.method
        })
        controller = new AbortController()
        config.signal = controller.signal
        config.controller = controller
        
        return config
      },
      (err) => {
        return err
      }
    )
    
    this.instance.interceptors.response.use(
      (res) => {
      
        //axios异步并发3
        // 清除当前记录
        if (pending && pending.length > 0) {
          for (let p in pending) {
            if(pending[p].u === res.config.url.split('?')[0] + '&' + res.config.method) {
              pending.splice(p, 1)
            }
          }
        }
        
        useLoadingData.showLoading = false
        return res
      },
      (err) => {
        //axios异步并发4
        pending = [] //清空记录

        // console.log("err", err)
        useLoadingData.showLoading = false
        // // return err
        return Promise.reject(err)
      }
    )
    
  }
  
  request(config) {
    if (config.sign) {
      config.data = {
        data: config.data || {}
      }
    }
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then((res) => {
          resolve(res.data || {})
        })
        .catch((err) => {
          reject(err)
        })
    })
  }
  
  get(config) {
    return this.request({ ...config, method: 'get' })
  }

  post(config) {
    return this.request({ ...config, method: 'post' })
  }

}

export default new Request(TIMEOUT)

2. CancelToken deprecated (不推荐)

您还可以使用 cancel token 取消一个请求。

Axios 的 cancel token API 是基于被撤销 cancelable promises proposal

此 API 从 v0.22.0 开始已被弃用,不应在新项目中使用。

2.1 在axios中声明数组记录标识

//axios异步并发1
let pending = [] //声明一个数组用于存储每个请求的标识
let cancelToken = axios.CancelToken

let removePending = (config) => {
  //查找数组中是否存在相同请求,存在则取消
  if (pending && pending.length > 0) {
    for (let p in pending) {
      if (pending[p].u === config.url.split('?')[0] + '&' + config.method) {
        if(pending[p].f){
          pending[p].f() //执行取消操作
        }
        pending.splice(p, 1) //数组移除当前请求
      }
    }
  }
}

2.2 在请求前校验拦截

class Request {
  constructor(baseURL, timeout = 10000) {
    this.instance = axios.create({
      baseURL: 'https://juejin.cn/creator/content/column?status=all',
      timeout,
      headers: {
        'Content-Type': 'application/json'
      }
    })
    
    this.instance.interceptors.request.use(
      (config) => {
      
        //axios异步并发2
        removePending(config) //在一个axios发送前执行校验取消操作
        config.cancelToken = new cancelToken((c) => {
          // pending存放每一次请求的标识,config.url请求路径,config.method请求方法
          pending.push({u: config.url.split('?')[0] + '&' + config.method, f: c});
        })

        return config
      },
      (err) => {
        return err
      }
    )
    
  }
}

2.3 在请求返回后维护声明数组pending

class Request {
  constructor(baseURL, timeout = 10000) {
    this.instance = axios.create({
      baseURL: 'https://juejin.cn/creator/content/column?status=all',
      timeout,
      headers: {
        'Content-Type': 'application/json'
      }
    })
    
    this.instance.interceptors.response.use(
      (res) => {
      
        //axios异步并发3
        // 清除当前记录
        if (pending && pending.length > 0) {
          for (let p in pending) {
            if(pending[p].u === res.config.url.split('?')[0] + '&' + res.config.method) {
              pending.splice(p, 1)
            }
          }
        }
        
        useLoadingData.showLoading = false
        return res
      },
      (err) => {
        //axios异步并发4
        pending = [] //清空记录

        // console.log("err", err)
        useLoadingData.showLoading = false
        // // return err
        return Promise.reject(err)
      }
    )
    
  }
}