axios防止重复请求以及取消请求

书中仙
2019.04.04 11:56 字数 2494 阅读 160 评论 0 喜欢 0

我们在日常开发中经常会遇到防止重复请求的情况,比如说点击一个按钮需要提交一些信息,但是ajax是异步的,在服务器给我们返回值的这个时间内,如果用户再次点击按钮,就会造成重复提交的情况,后台就有可能会插入两条数据,这种情况我们当然是不希望看到的,关于防止重复提交前端和后端都有很多种解决方案,我们今天就来探讨一下前端的解决方案

其实防止重复提交非常简单,最简单的做法是我们定义一个变量flag=true,在发送ajax之前判断flag的状态是否为true,如何不是就return掉,如果是就将flag的值置为false,在ajax请求结束之后将flag置为true,这样就达到了防止重复提交的目的,但是这种方法并不好,一是要定义一个额外的变量,二是书写非常麻烦,尤其一个页面有可能好多个操作,难道我们还能每个ajax都这么写吗?

然后我今天就想了,是否能在axios的request拦截器里统一判断是重复请求,如果是重复请求就拒绝发送

我们认为请求路径、请求方法、请求参数完全一致的的请求为重复请求

let pending = [] /* 声明用于存储每个ajax标识的数组 */
let judgePending = (config) => { /* 判断此条请求是否在数组中 */
for(let p in pending) {
if(pending[p].path === config.url + '&' + config.method + '&' + JSON.stringify(config.params)) { /* 当当前请求在数组中存在时 */
return false /* 重复的请求 */
}
}
return true /* 未重复的请求 */
}
let removePending = (config) => { /* 去除已经发送过的请求 */
for(let p in pending) {
if(pending[p].path === config.url + '&' + config.method + '&' + JSON.stringify(config.params)) { /* 当当前请求在数组中存在时 */
pending.splice(p, 1); /* 把这条记录从数组中移除 */
break; /* 跳出for循环 */
}
}
}
/* request拦截器 */
api.interceptors.request.use(
async config => {
if(judgePending(config)) { /* 验证通过 */
pending.push({ path: config.url + '&' + config.method + '&' + JSON.stringify(config.params)});
/* 二维的对象我们不好判断是否是相等,因此可以转换为字符串方便判断(注意这种方法只是取巧,并不能判断对象完全相等)*/
return config
}else {
return Promise.reject(new Error('repeat request'))
}
},
error => {
return Promise.reject(error)
}
)

/* respone拦截器 */
api.interceptors.response.use(
res => {
removePending(res.config) /* 请求结束之后将该条请求从数组中释放 */
res.data.ok = res.data.status_code === 200
return res
},
error => {
removePending(error.config) /* 注意error的请求也要释放出来 */
return Promise.reject(error)
}
)

然后我们还可以进一步优化,将取消请求的方法push进pending里

/* request拦截器 */
api.interceptors.request.use(
async config => {
if(judgePending(config)) { /* 验证通过 */
config.cancelToken = new cancelToken((c)=>{
pending.push({ path: config.url + '&' + config.method + '&' + JSON.stringify(config.params),f: c});
/* 给pending里的ajax标识增加一个f属性,可以取消请求 */
});
return config
}else {
this.message.error('请勿重复操作') /* 当有重复请求时可以进行提示 */
return Promise.reject(new Error('repeat request'))
}
},
error => {
return Promise.reject(error)
}
)

然后将取消请求的方法导出

export function abortRequest () {
for(let p in pending) {
pending[p].f() /* 取消请求 */
}
pending = [] /* 将数组置空 */
}

在路由切换的时候可以执行这个方法,取消掉无用的请求。


支付二维码
登录 后发表评论
${comment_count}条评论 评论

智慧如你,不想发表一点想法咩~

推荐阅读
更多精彩内容