本文详解Axios中利用CancelToken取消请求及防止重复提交的方法。通过对比两种Token创建方式,重点讲解如何在拦截器中统一管理请求列表,实现请求去重与取消逻辑,解决用户快速点击导致的重复请求问题,并提供相关代码实现细节。
前言
在实际项目开发中,经常需要对网络请求进行“防抖”处理。主要目的是防止用户在特定场景下短时间内多次点击按钮,导致前端向后端发送大量重复请求。以下列举两种常见场景:
- PC端场景中,用户双击搜索按钮可能触发两次搜索请求
- 移动端场景中,由于缺乏点击延迟机制,极易因误操作或多操作导致请求重发
即便页面显示Loading遮罩,上述情况仍可能发生,因此需要在前端层面实现阻止重复请求的机制。
核心——CancelToken
Axios中取消请求的核心机制是CancelToken。官方文档提供了两种使用方式,此处简要列出并添加注释说明:
方法1:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
// 必须对请求进行cancelToken设置
cancelToken: source.token
}).catch(function (thrown) {
// 如果请求被取消则进入该方法判断
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
// 取消上面的请求
// source.cancel('messge') message为可选项,必须为String
source.cancel('Operation canceled by the user.');
方法2:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
// 在options中直接创建一个cancelToken对象
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
// 取消上面的请求
cancel();
实际应用和封装
前文已举例说明Axios中的核心方法,但在实际工程中,我们通常不会直接照搬官方示例,而是通过Axios拦截器进行全局配置管理。因此,需要对上述代码进行适配改造。
实现思路概述:
- 需缓存所有正在进行中的请求。在发起新请求前,检查缓存列表中是否存在相同请求,若存在则取消当前请求。
- 任意请求完成后,需从缓存列表中移除该请求,以便后续可重新发送相同请求。
思路阐述完毕,接下来提供具体代码实现。
// 正在进行中的请求列表
let reqList = []
/**
* 阻止重复请求
* @param {array} reqList - 请求缓存列表
* @param {string} url - 当前请求地址
* @param {function} cancel - 请求中断函数
* @param {string} errorMessage - 请求中断时需要显示的错误信息
*/
const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
const errorMsg = errorMessage || ''
for (let i = 0; i < reqList.length; i++) {
if (reqList[i] === url) {
cancel(errorMsg)
return
}
}
reqList.push(url)
}
/**
* 允许某个请求可以继续进行
* @param {array} reqList 全部请求列表
* @param {string} url 请求地址
*/
const allowRequest = function (reqList, url) {
for (let i = 0; i < reqList.length; i++) {
if (reqList[i] === url) {
reqList.splice(i, 1)
break
}
}
}
const service = axios.create()
// 请求拦截器
service.interceptors.request.use(
config => {
let cancel
// 设置cancelToken对象
config.cancelToken = new axios.CancelToken(function(c) {
cancel = c
})
// 阻止重复请求。当上个请求未完成时,相同的请求不会进行
stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`)
return config
},
err => Promise.reject(err)
)
// 响应拦截器
service.interceptors.response.use(
response => {
// 增加延迟,相同请求不得在短时间内重复发送
setTimeout(() => {
allowRequest(reqList, response.config.url)
}, 1000)
// ...请求成功后的后续操作
// successHandler(response)
},
error => {
if (axios.isCancel(thrown)) {
console.log(thrown.message);
} else {
// 增加延迟,相同请求不得在短时间内重复发送
setTimeout(() => {
allowRequest(reqList, error.config.url)
}, 1000)
}
// ...请求失败后的后续操作
// errorHandler(error)
}
)
一些小细节
为何未采用前文方法2中的代码进行CancelToken设置? Axios文档中有一条重要备注:
Note: you can cancel several requests with the same cancel token. 你可以使用相同的Token来取消多个请求
因此,我不希望在每次请求前都new一个新的对象。 请务必使用方法2,确保每次cancel都能正确执行。若使用方法1,当出现cancel后,后续请求也会持续被cancel。
为何在response中需要增加延迟? 目的是避免用户在极短时间内重复发起相同请求。 请注意,在response中阻止请求与在request中阻止请求是两个不同的概念: request中是阻止上个请求未完成时又开始了相同的请求 response中是阻止上个请求完成后一段时间内不允许相同请求
我能否在cancel时传递一个对象,而不仅仅是message? 以官方提供的接口来看是不可以的,你可以仿照官方源码进行重新封装,或者使用第三方插件,或者使用另一个方法:将对象转换为JSON字符串,然后在需要的地方再转换回来
到此这篇关于浅谈axios中取消请求及阻止重复请求的方法的文章就介绍到这了,更多相关axios中取消请求及阻止重复请求内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!