我在网上找了一个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
很明显他们根本不一样所以pendingRequest.delete(requestKey)的时候map肯定移除不了此请求,而且根据打印的结果很明显是GenerateReqKey()函数在响应生成的requestKey时比添加生成的requestKey多JSON.stringify()了一次
继续分析他们在执行GenerateReqKey()时传入的config是否一致
我设置一个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 }