需求描述
限制用户多次点击按钮,频繁地发送同一个请求,影响页面渲染效果,降低前端的无效接口请求操作(其中涉及到Map、Array、Promise的一些基本操作)。
解决方案
由于所接触的是Vue项目,项目中使用的接口请求工具为axios,因此,采用编写前端拦截器的方式来对除第一次之外的——多个处于pending等待状态的同一个无效请求进行过滤,即:只有首次发送的请求会被正常处理,后续的会被直接过滤掉。
基本思路
使用一个Map集合对象,保存用户发送的每个请求。Map中每一个元素的requestKey唯一标识值由请求的method、url和data参数构成。
axios拦截器分别为:请求拦截器和响应拦截器,其中,
①请求拦截器:在进行接口数据请求时,判断是否已经有相同的请求在执行(Map集合中是否存在由requestKey标识的请求对象),如果有,就直接取消掉当前一次请求;如果没有,则正常执行。
②响应拦截器:在正常执行完毕一次接口请求时,就再次获取该请求的requestKey标识值,并根据requestKey标识值从Map集合中移除,以确保下一次接口请求的正常执行。
代码编写
核心代码
//记录处理pending状态的Axios请求对象
var pendingRequestMap = new Map();
//拦截器配置
//1-请求拦截器配置
axios.interceptors.request.use(function (config) {
console.warn("请求拦截器...");
//校验-method|url|data
let requestKey = `${config.method}-${config.url}-${config.data}`;//创建request请求key
let cancelFunction = undefined;
config.requestKey = requestKey;//挂载requestKey到config对象上
//获取axios取消请求回调函数
config.cancelToken = new axios.CancelToken((cancel)=>{
cancelFunction = cancel;
});//创建cancelToken
console.log(config);
//判断当前pendingRequestMap中是否存在当前requestKey
if(pendingRequestMap.has(requestKey)){
//执行请求拦截-取消当前一次的请求
cancelFunction();
}else {
//保存当前一次的请求
pendingRequestMap.set(requestKey,config);
}
return Promise.resolve(config);//改变config状态为resolved
},function (error) {
return Promise.reject(error);
});
//2-响应拦截器配置
axios.interceptors.response.use(function (response) {
console.warn("响应拦截器...");
let requestKey = response.config.requestKey ;//获取当前请求的Key值
//如果执行成功-根据key值,从pendingRequestMap中移除当前请求config
pendingRequestMap.delete(requestKey);
return Promise.resolve(response);//改变响应结果状态为resolved
},function (error) {
return Promise.reject(error);//改变响应结果状态为rejected
});
完整代码
以示例demo的形式给出。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AxiosTest</title>
<script src="./axios/axios.min.js"></script>
</head>
<body>
<button id="sendAjax">发送请求</button>
</body>
<script>
//记录处理pending状态的Axios请求对象
var pendingRequestMap = new Map();
//拦截器配置
//1-请求拦截器配置
axios.interceptors.request.use(function (config) {
console.warn("请求拦截器...");
//校验-method|url|data
let requestKey = `${config.method}-${config.url}-${config.data}`;//创建request请求key
let cancelFunction = undefined;
config.requestKey = requestKey;//挂载requestKey到config对象上
//获取axios取消请求回调函数
config.cancelToken = new axios.CancelToken((cancel)=>{
cancelFunction = cancel;
});//创建cancelToken
console.log(config);
//判断当前pendingRequestMap中是否存在当前requestKey
if(pendingRequestMap.has(requestKey)){
//执行请求拦截-取消当前一次的请求
cancelFunction();
}else {
//保存当前一次的请求
pendingRequestMap.set(requestKey,config);
}
return Promise.resolve(config);//改变config状态为resolved
},function (error) {
return Promise.reject(error);
});
//2-响应拦截器配置
axios.interceptors.response.use(function (response) {
console.warn("响应拦截器...");
let requestKey = response.config.requestKey ;//获取当前请求的Key值
//如果执行成功-根据key值,从pendingRequestMap中移除当前请求config
pendingRequestMap.delete(requestKey);
return Promise.resolve(response);//改变响应结果状态为resolved
},function (error) {
return Promise.reject(error);//改变响应结果状态为rejected
});
//事件注册-发送Axios请求
var btn = document.getElementById("sendAjax");
btn.addEventListener("click",function (ev) {
axios({
method: 'get',
url: '/shqrpweb/QueryShort/loadChangzhu/test1',
data: {
code:"310101"
}
}).then(function (result) {
console.log(result)
});
});
</script>
</html>
案例翻转
上述代码是对同一个接口的多次请求进行过滤,只保留第一次请求的实现。那么,如果指向保留最后一次请求,而取消掉前几次的请求呢?示例代码如下,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AxiosTest</title>
<script src="./axios/axios.min.js"></script>
</head>
<body>
<button id="sendAjax">发送请求</button>
</body>
<script>
//拦截器配置
//记录已经发起的所有请求
var requestPool = new Array();
//1-请求拦截器配置
axios.interceptors.request.use(function (config) {
console.warn("请求拦截器...");
//校验-method|url|data
let requestKey = `${config.method}-${config.url}-${config.data}`;//创建request请求key
if(requestPool&&requestPool.length>0){
//先判断是否存在相同的请求
requestPool.forEach((e,index)=>{
if(e['key'] === requestKey){
e['value'].cancel();//存在相同的请求-执行取消操作
delete requestPool[index];//从请求池中移除对应的请求
}
});
requestPool = requestPool.filter(item=>typeof item !== "undefined");//过滤掉空白元素
}
config.requestKey = requestKey; //挂载requestKey到config上
//将取消请求的cancelToken挂载到config实例上
console.log(config);
config.cancelToken = new axios.CancelToken((cancel)=>{
config.cancel = cancel;
});//创建cancelToken
//将当前请求添加到请求池中
requestPool.push({
key:requestKey,
value:config,
});
//将当前config状态转换为resolved
return Promise.resolve(config);
},function (error) {
return Promise.reject(error);
});
//2-响应拦截器配置
axios.interceptors.response.use(function (response) {
console.warn("响应拦截器...");
let requestKey = response.config.requestKey ;//获取当前请求的Key值
//如果执行成功-根据key值,从请求池requestPool中移除当前请求config
requestPool = requestPool.filter(item=>item['key']===requestKey);
console.log(requestPool);
return Promise.resolve(response);//改变响应结果状态为resocancelTokelved
},function (error) {
return Promise.reject(error);//改变响应结果状态为rejected
});
//事件注册-发送Axios请求
var btn = document.getElementById("sendAjax");
btn.addEventListener("click",function (ev) {
axios({
method: 'get',
url: '/shqrpweb/QueryShort/loadChangzhu/test1',
data: {
code:"310101"
}
}).then(function (result) {
console.log(result)
});
});
</script>
</html>