我在网上找了一个axios解决重复请求的代码

import axios from 'axios'
axios.defaults.baseURL = 'http://124.93.196.45:10001/'
const pendingRequest = new Map()

//`generateReqKey`:用于根据当前请求的信息,生成请求 Key;
function generateReqKey(config: any) {
  const { method, url, params, data } = config
  return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}

//`addPendingRequest:用于把当前请求信息添加到pendingRequest对象中;
function addPendingRequest(config: any) {
  const requestKey = generateReqKey(config)
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel: any) => {
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, cancel)
      }
    })
}

//`removePendingRequest`:检查是否存在重复请求,若存在则取消已发的请求。
function removePendingRequest(config: any) {
  const requestKey = generateReqKey(config)
  if (pendingRequest.has(requestKey)) {
    const cancelToken = pendingRequest.get(requestKey)
    cancelToken(requestKey)
    pendingRequest.delete(requestKey)
  }
}
function clearPending() {
  for (const [requestKey, cancelToken] of pendingRequest) {
    cancelToken(requestKey)
  }
  pendingRequest.clear()
}
axios.interceptors.request.use(
  function (config: any) {
    removePendingRequest(config) // 检查是否存在重复请求,若存在则取消已发的请求
    addPendingRequest(config) // 把当前请求信息添加到pendingRequest对象中

    return config
  },
  (error) => {
    // 这里出现错误可能是网络波动造成的,清空 pendingRequests 对象
    pendingRequest.clear()
    return Promise.reject(error)
  }
)
axios.interceptors.response.use(
  (response) => {
    removePendingRequest(response.config) // 从pendingRequest对象中移除请求
    return response
  },
  (error) => {
    removePendingRequest(error.config || {}) // 从pendingRequest对象中移除请求
    if (axios.isCancel(error)) {
      console.warn(error)
      return Promise.reject(error)
    } else {
      // 添加其它异常处理
    }
    return Promise.reject(error)
  }
)
//通过请求拦截器实现如果有token自动添加token,如果没有token就不携带token
export default axios
export {clearPending}
//添加全局路由守卫,在跳转时自动清空PendingRequest
router.beforeEach((to, from, next) => {
  clearPending();
})

但是这种方式他是取消之前相同的请求保留最后一次请求,于是会出现不好的体验:例如我疯狂点击登录按钮,他会一直登录不进去(因为上次一请求都未完成就被取消了开启新的请求,如此反复一直在请求没有响应)

于是我准备修改一下,根据他的代码改成如果是相同的请求(请求参数相同),我就保留第一次的请求,取消当前发送的请求

1.引入取消请求的语句
+  const CancelToken = axios.CancelToken;
+  const source = CancelToken.source();
2.我修改了removePendingRequest()函数
function removePendingRequest(config: any) {
  const requestKey = generateReqKey(config)
  if (pendingRequest.has(requestKey)) {
  //删除原来用于取消之前重复请求的语句
-    const cancelToken = pendingRequest.get(requestKey)
-    cancelToken(requestKey)
-    pendingRequest.delete(requestKey)
//添加自己的取消请求语句:取消当前请求
+    config.cancelToken = source.token;
+     source.cancel()   
  }
}
3.修改响应拦截器移除请求的语句
axios.interceptors.response.use(
  (response) => {
     //原本用于移除的函数被我们修改了所以这里移除请求不起作用,必须自己重新编写移除的语句
-    removePendingRequest(response.config)
     //重新获取响应的config自己手动在map里移除一次该请求
+    const requestKey = GenerateReqKey(response.config)
+    pendingRequest.delete(requestKey) // 从pendingRequest对象中移除请求
    return response
  },
  (error) => {
    removePendingRequest(error.config || {}) // 从pendingRequest对象中移除请求
    if (axios.isCancel(error)) {
      console.warn(error)
      return Promise.reject(error)
    } else {
      // 添加其它异常处理
    }
    return Promise.reject(error)
  }
)

如此下来确实可以实现如果是相同的请求(请求参数相同)保留第一次的请求,取消当前发送的请求 但是出现了一个bug:等待第一次请求完成还是发送不了当前相同参数的请求 原因是响应后本来应该在响应完成的时候在map中去除该请求但是却没移除成功了

于是我打印了一下添加至map时requestKey的响应生成的requestKey

axios取消提示 axios取消重复请求_javascript

很明显他们根本不一样所以pendingRequest.delete(requestKey)的时候map肯定移除不了此请求,而且根据打印的结果很明显是GenerateReqKey()函数在响应生成的requestKey时比添加生成的requestKey多JSON.stringify()了一次

继续分析他们在执行GenerateReqKey()时传入的config是否一致

axios取消提示 axios取消重复请求_axios取消提示_02

axios取消提示 axios取消重复请求_javascript_03

axios取消提示 axios取消重复请求_axios取消提示_04

axios取消提示 axios取消重复请求_node.js_05

我设置一个add和res用来放添加时的config和响应时的config,判断他们是否相同,返回了true显示一模一样,一模一样的参数到了gererateReqKey为函数为什么返回却不一样?这个问题我也不是很清楚,但是我已经有了解决方案了

直接设置两个不一样的生成requestKey的函数,判断是添加时要生成还是响应回来时要生成
如果添加就调用添加的函数
如果响应就调用响应的函数

//正常的
function generateReqKey(config: any) {
  const { method, url, params, data } = config
  return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
} 
//响应的(不再执行JSON.stringify())
function resGenerateReqKey(config: any) {
  const { method, url, params, data } = config
  return [method, url, params, data].join('&')
}

于是就可以成功地在响应后map里移除请求了

下面是全部的源代码

import axios from 'axios'
axios.defaults.baseURL = 'http://124.93.196.45:10001/'
const pendingRequest = new Map()
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
function generateReqKey(config: any) {
  const { method, url, params, data } = config
  return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
} function resGenerateReqKey(config: any) {
  const { method, url, params, data } = config
  return [method, url, params, data].join('&')
}

function addPendingRequest(config: any) {
  const requestKey = generateReqKey(config)
  console.log(requestKey, 'add');

  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel: any) => {
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, cancel)
      }
    })
}
function removePendingRequest(config: any) {
  const requestKey = generateReqKey(config)
  if (pendingRequest.has(requestKey)) {

    config.cancelToken = source.token;
    source.cancel()

  }
}
function clearPending() {
  for (const [requestKey, cancelToken] of pendingRequest) {
    cancelToken(requestKey)
  }
  pendingRequest.clear()
}
axios.interceptors.request.use(
  function (config: any) {
    if (localStorage.token) {
    //通过请求拦截器实现如果有token自动添加token,如果没有token就不携带token
      config.headers['Authorization'] = `Bearer ${localStorage.token}`
    }
    removePendingRequest(config) // 检查是否存在重复请求,若存在则取消已发的请求
    addPendingRequest(config) // 把当前请求信息添加到pendingRequest对象中

    return config
  },
  (error) => {
    // 这里出现错误可能是网络波动造成的,清空 pendingRequests 对象
    pendingRequest.clear()
    return Promise.reject(error)
  }
)
axios.interceptors.response.use(
  (response) => {
    const requestKey = resGenerateReqKey(response.config)

    pendingRequest.delete(requestKey)
    // 从pendingRequest对象中移除请求
    if (response.data.code != 200) {
      alert(response.data.msg)
    }
    return response
  },
  (error) => {
    removePendingRequest(error.config || {}) // 从pendingRequest对象中移除请求
    if (axios.isCancel(error)) {
      console.warn(error)
      return Promise.reject(error)
    } else {
      // 添加其它异常处理
    }
    return Promise.reject(error)
  }
)

export default axios
export { clearPending }