Bootstrap

vue axios 阻塞未完成请求

import _ from 'lodash'
import axios from 'axios'
const { CancelToken, Cancel } = axios
const tokens = {}

/**
 * 请求前处理 config(尽可能在最先注册的拦截器中调用)
 * @param {import('axios').AxiosRequestConfig} config
 */
export const setConfig = function(config) {
  const { method, baseURL = '', url, exCancelName, cancelToken } = config
  const path = url.replace(/\?.*$/, '')

  let name = `${method}${baseURL}${path}`
  if (exCancelName === true) name = `${method}${baseURL}${path}`
  else if (/^\//.test(exCancelName)) name = `${method}${baseURL}${exCancelName}`
  else if (typeof exCancelName === 'string' && exCancelName) name = exCancelName

  let token, _promise
  if (cancelToken instanceof CancelToken) {
    token = cancelToken
    _promise = cancelToken.promise
  } else {
    token = new CancelToken(_.noop)
  }
  token.promise = new Promise(resolve => {
    _promise && _promise.then(resolve).catch(e => e)
    token._exCancel_resolvePromise = resolve
  })
  token._exCancel_id = _.uniqueId(`${Date.now()}`)
  config._exCancel.id = token._exCancel_id
  config._exCancel.name = name
  config.cancelToken = token
  tokens[name] = tokens[name] || []
  tokens[name].push(token)
}

/**
 * 钩子函数
 * @type {import('axios').AxiosInstance['exHooks'][0]}
 */
export const hooks = Object.freeze({
  onBefore(config) {
    config._exCancel = {} // 钩子与拦截器之间的通信对象
    if (config.exCancel) {
      const { method, baseURL = '', url, exCancel } = config
      const path = url.replace(/\?.*$/, '')

      let names = Array.isArray(exCancel) ? exCancel : [exCancel]
      names = _.union(names).map(name => {
        if (name === true) return `${method}${baseURL}${path}`
        if (/^\//.test(name)) return `${method}${baseURL}${name}`
        if (typeof name === 'string') return name
      })
      names = names.filter(Boolean)
      cancel(names)
    }
  },
  onComplete(config) {
    const { id, name } = config._exCancel
    const arr = tokens[name]
    if (id && arr) {
      const index = arr.findIndex(token => token._exCancel_id === id)
      if (index > -1) {
        arr.splice(index, 1)
        if (arr.length === 0) delete tokens[name]
      }
    }
  },
})

/**
 * 取消未完成的请求
 * @param {string | string[]} name exCancelName 参数值对应的名称
 */
export const cancel = function(name) {
  const names = Array.isArray(name) ? name : [name]
  _.each(tokens, (val, key) => {
    if (names.includes(key)) {
      val.forEach(token => {
        if (token.reason) return
        token.reason = new Cancel('exCancel')
        token._exCancel_resolvePromise(token.reason)
      })
      delete tokens[key]
    }
  })
}

;