axiosCancelToken取消频繁发送请求的⽤法和源码解析
前⾔
做⼀个Vue的项⽬时,遇到频繁切换标签的问题。由于不同标签请求的ajax的结果所需时间不同,点击不同标签时,响应时间最慢的数据会覆盖之前响应的数据,显⽰数据跟所点击标签不对应。当时为了处理这个问题,没想到好⽅法,只好控制在点击下⼀个标签前,必须等前⼀个标签的结果回来之后进⾏。
后来做API的统⼀管理时,看到前⼈写的axios的interceptor⾥有CancelToken这样⼀个东西,查了查资料,发现这个可以取消请求,踏破铁鞋⽆觅处,刚好可以⽤来处理之前遇到的频繁切换标签的问题。今作⼀记录,也好更好的理解这个功能。
述求
点击标签时,取消之前正在执⾏的请求,使得切换标签时,页⾯得到的是最后请求的结果,⽽不是响应最慢的结果。
⽤法
官⽅案例
1. 使⽤ CancelToken.source ⼯⼚⽅法创建 cancel token,像这样:
// CancelToken是⼀个构造函数,⽤于创建⼀个cancelToken实例对象
// cancelToken实例对象包含了⼀个promi属性,值为可以触发取消请求的⼀个promi
const CancelToken = axios.CancelToken;
// 执⾏source()得到的是⼀个包含了cancelToken对象和⼀个取消函数cancel()的对象
// 即 source = {token: cancelToken对象, cancel: 取消函数}
const source = CancelToken.source();
// 在请求的配置中配置cancelToken,那么这个请求就有了可以取消请求的功能
<('/ur/12345',{
有丝分裂前期cancelToken: ken
}).catch(function(thrown){
if(axios.isCancel(thrown)){
console.log('Request canceled', ssage);
}el{
// 处理错误
}
});
axios.post('/ur/12345',{
name:'new name'
},{
cancelToken: ken
})
/
/ 执⾏取消请求(message 参数是可选的)
source.cancel('Operation canceled by the ur.');
2. 通过传递⼀个 executor 函数到 CancelToken 的构造函数来创建 cancel token
const CancelToken = axios.CancelToken;
let cancel;
<('/ur/12345',{
cancelToken:new CancelToken(function executor(c){
// executor 函数接收⼀个 cancel 函数作为参数
// 把cancel函数传递给外⾯,使得外⾯能控制执⾏取消请求
cancel = c;
})
});
// cancel the request
cancel();
看起来有些晕,毕竟不知道⾥⾯是怎么运作的,稍后通过源码解析。这⾥简单解释就是,在请求中配置cancelToken这个属性,是为了使得请求具有可以取消的功能;cancelToken属性的值是⼀个CancelToken实例对象,在它的executor函数中提取出cancel函数,执⾏这
个cancel函数来取消请求。
我的实例
点击标签,执⾏getCour函数。点击某个标签时,先取消之前的请求(如果之前的请求已完成,取消请求不会有任何操作)。效果是,页⾯显⽰的总是最后点击的标签对应的结果。
分两步,第⼀步,在get请求中配置cancelToken属性,开启取消请求的功能,且在其属性值中将cancel函数赋给cancelRequest,使得外部可以调⽤cancel函数来取消请求;第⼆步,在执⾏请求前,先取消前⼀次的请求。
import axios from'axios'
export default{
data(){
return{
cancelRequest:null// 初始时没有请求需要取消,设为null }
},
methods:{
// 点击标签后发送请求的函数
getCour(){
const that =this
// 2. 准备执⾏新的请求前,先将前⼀个请求取消
/
/ 如果前⼀个请求执⾏完了,执⾏取消请求不会有其他操作
if(typeof that.cancelRequest ==='function'){
that.cancelRequest()
}
// 这⾥配置请求的参数,略
let params ={}
// 发送请求
去颈纹最有效的方法('/api/app/cour',{
params: params,
cancelToken:new CancelToken(function executor(c){ // 1. cancel函数赋值给cancelRequest属性
// 从⽽可以通过cancelRequest执⾏取消请求的操作 that.cancelRequest = c
})
})
}
}
}
⼀般API都会统⼀封装,所以,可以将请求封装起来
API
// /api/modules/cour.js
// _this为vue组件实例对象
export function getCourReq(params, _this){
('/api/app/cour',{
params: params,
cancelToken:new CancelToken(function executor(c){ // 1. cancel函数赋值给cancelRequest属性
// 从⽽可以通过cancelRequest执⾏取消请求的操作
_this.cancelRequest = c
})
})
.then(res =>{})
.catch(err =>{})
}
组件
import{ getCourReq }from'@/apis/modules/cour'
methods:{
getCour(){
// 2. 准备执⾏新的请求前,先将前⼀个请求取消
// 如果前⼀个请求执⾏完了,执⾏取消请求不会有其他操作
if(typeof this.cancelRequest ==='function'){
this.cancelRequest()
}
// 这⾥配置请求的参数,略
let params ={}
// 发送请求
getCourReq(params,this)
.
then(res =>{})
.catch(err =>{})
}
}
遇到的坑
⼀开始按照上述⽅法写好,但请求死活没有取消。⼀遍遍核对了变量名,调试输出信息,啥都对,就是没法取消。折腾半天,想起前⼈配置的axios的interceptor⾥对每个请求配置了cancelToken,⽬的是为了去掉重复的请求,但取消重复请求的代码段被注释掉了。
把cancelToken注释掉之后,终于⾬过天晴。
quest.u(
config =>{
const request =
JSON.stringify(config.url)+
JSON.hod)+
我深爱的你JSON.stringify(config.data ||'')
// 这⾥配置了cancelToken属性,覆盖了原请求中的cancelToken
config.cancelToken =new CancelToken(cancel =>{
sources[request]= cancel
})
// if (requestList.includes(request)) {
// sources[request]('取消重复请求')
// } el {
requestList.push(request)
return config
},
error =>{
ject(error)
}保持健康用英语怎么说
)
血块多是什么原因由于interceptor会在请求发送前做⼀些配置处理,这⾥把原请求中的cancelToken覆盖了,那么即使原请求中执⾏
原cancelToken的cancel函数,由于cancelToken对象不同了,取消操作也就⽆效了。后⾯看了源码可以更明⽩为什么⽆效。
源码解析
根据前⾯的步骤,依次来看看各个源码是怎样。
⾸先,我们为请求配置cancelToken属性,⽬的是使得请求具有能取消请求的功能,它的值是CancelToken实例对象,那么CancelToken是什么呢?
// axios/lib/cancel/CancelToken.js
'u strict';
var Cancel =require('./Cancel');
画中秋节function CancelToken(executor){
if(typeof executor !=='function'){
throw new TypeError('executor must be a function.');
}
/**
* 定义⼀个将来能执⾏取消请求的promi对象,当这个promi的状态为完成时(fullfilled),
* 就会触发取消请求的操作(执⾏then函数)。⽽执⾏resolve就能将promi的状态置为完成状态。
* 这⾥把resolve赋值给resolvePromi,就是为了在这个promi外能执⾏resolve⽽改变这个promi的状态
* 注意这个promi对象被赋值给CancelToken实例的属性promi,将来定义then函数就是通过这个属性得到promi
*/
var resolvePromi;
this.promi =new Promi(function promiExecutor(resolve){
resolvePromi = resolve;
});
/**
* 将CancelToken实例赋值给token
* 执⾏executor函数,将cancel⽅法传⼊executor,
* cancel⽅法可调⽤resolvePromi⽅法,即触发取消请求的操作
*/
var token =this;
executor(function cancel(message){
ason){
// 取消已响应返回
return;
}两个小娃娃
// 这⾥执⾏的就是promi的resolve⽅法,改变状态
ason);
});
}
CancelToken.prototype.throwIfRequested=function throwIfRequested(){
ason){
ason;
}
};
// 这⾥可以看清楚source函数的真⾯⽬
CancelToken.source=function source(){
var cancel;
var token =new CancelToken(function executor(c){
/
/ c 就是CancelToken中给executor传⼊的cancel⽅法
cancel = c;
});
return{
token: token,
cancel: cancel
};
};
CancelToken
CancelToken是⼀个构造函数,通过new CancelToken()得到的是⼀个实例对象,它只有⼀个属性promi, 它的值是⼀个能触发取消请求的Promi对象。
胆结石能喝酒吗
token = new CancelToken(executor function) ===> { promi: Promi对象 }