axios取消重复请求的文章很多,大部分都是用caceltoken取消前面的请求。但实际上大部分业务模式下,取消重复请求有时需要取消前面的重复请求,有时呢又应该取消后面的重复请求。举个例子,根据用户输入在onchange搜索时,就应该取消前面的请求防止前面的请求后返回,导致页面展示非预期。而当有些用户操作,比如删除新增时,忘记给按钮加loading而接口又比较慢时,导致多新增了数据,或删除时接口报错。这时更应该取消后面的重复请求。
首先我会在接口调用时传入参数,通过传入cancelType来控制取消前面还是后面的请求。
<script>
import httpRequest from '../utils/request'
export default {
name: 'app',
components: {
},
methods: {
cancelBefore(){
httpRequest.get('/GetPagerList',{},{cancelType: 'before'})
},
cancelafter(){
httpRequest.post('/Web/yuyue',{},{cancelType: 'after'})
}
}
}
取消前面的pengding请求核心是CancelToken,这个就不多说了
import axios from 'axios'
const CancelToken = axios.CancelToken
class HttpRequest {
constructor() {
this.baseURL = 'http://zj.jxeduyun.com/Web/'
this.pending = {}
}
// 获取预设的配置
getInsideConfig() {
return {
baseURL: this.baseURL,
headers: { 'Content-Type': 'application/json;charset=utf-8' },
timeout: 10000,
}
}
// 取请求url、method、params、data等组成key,标识这次请求
getPendingKey(config) {
return [
config.url,
config.method,
JSON.stringify(config.params),
JSON.stringify(config.data),
].join('&')
}
// 移除pending请求,isFront 是否取消前面的请求
removeKey(key, isFront = false) {
if (this.pending[key] && isFront) {
this.pending[key]('取消前面的重复请求')
}
delete this.pending[key]
}
而取消后面的请求核心是直接reject
// 拦截处理
interceptors(instance) {
// 添加请求拦截器
instance.interceptors.request.use(
config => {
// 1、取请求标识key
// 2、移除上一次该请求标识(如果有,则调用cancelToken取消上次请求)
// 3、给这次请求增加cancelToken备用(如果后续有重复请求则调用)
if(config.cancel){
const cancelType = config.cancel
const key = this.getPendingKey(config)
// 取消前面的重复请求
if(cancelType==='before'){
this.removeBefore(key, true)
config.cancelToken = new CancelToken(cancel => {
this.pending[key] = cancel
})
return config
}else if(cancelType==='after'){
// 取消后面的请求直接reject,要把key传下去,好在前面请求失败的时候也移除
if(this.pending[key]){
return Promise.reject({key,msg:'取消后面的请求'})
}else{
this.pending[key]='after' //
return config
}
}
}
else{
return config
}
},
error => {
// 对请求错误做些什么
console.log(error)
return Promise.reject(error)
}
)
// 添加响应拦截器
instance.interceptors.response.use(
response => {
// 请求返回结果,移除pending
const key = this.getPendingKey(response.config)
this.removeKey(key)
if (response.status === 200) {
return Promise.resolve(response.data)
} else {
return Promise.reject(response)
}
},
error => {
// 取消后面重复请求 响应错误同样要移除key
if(error.key){
this.removeKey(error.key)
}
// 对响应错误做些什么
console.log(error.msg || error)
return Promise.reject(error)
}
)
}
取消后面的重复请求时,判断pending是否有请求key,有直接reject。没有就加上,return config。特别需要注意的是取消后面的重复请求时。一定要在error时也从pending中删除。否则当接口报错时,无法继续请求。
特别注意的是,通过cancelToken取消请求,请求依然是会发送到后端的,如果后端没做处理的话,依然该干嘛干嘛。只是前端不再拿数据而已,如果要防止重复操作,用取消后面的请求更好,前面一个请求没结束的时候直接就不发起请求。 当然如果接口是严格按照rustful风格的话,只需要通过method判断,就不用传参了,get全部取消前面的重复请求,post、delete、put直接取消后面的重复请求.那将绝杀,可惜。
最后是封装的一些方法
// 发送请求
request(config) {
const instance = axios.create()
const newOptions = Object.assign(this.getInsideConfig(), config)
this.interceptors(instance)
return instance(newOptions)
}
get(url,data,config) {
const options = Object.assign(
{
url,
params: data,
method: 'get'
},
config
)
return this.request(options)
}
post(url, data, config) {
const options = Object.assign(
{
url,
data,
method: 'post'
},
config
)
return this.request(options)
}
// 取消全部pending请求
cancelAll(key) {
for(key in this.pending){
this.removeKey(key, true)
}
// 清空pengding
this.pending = {}
}
}
export default new HttpRequest()
cancelAll方法会取消所有pengding接口,可以在spa中路由跳转进入之前调用,减少不必要的网络开支。比如vue的beforeEach 中调用。这就是全部内容了,如果有更好的方案或想法欢迎留言讨论。