axiosCancelToken取消频繁发送请求的⽤法和源码解析
前⾔
做⼀个Vue的项⽬时,遇到频繁切换标签的问题。由于不同标签请求的ajax的结果所需时间不同,点击不同标签时,响应时间最慢的数据会覆盖之前响应的数据,显⽰数据跟所点击标签不对应。当时为了处理这个问题,没想到好⽅法,只好控制在点击下⼀个标签前,必须等前⼀个标签的结果回来之后进⾏。
后来做API的统⼀管理时,看到前⼈写的axios的interceptor⾥有CancelToken这样⼀个东西,查了查资料,发现这个可以取消请求,踏破铁鞋⽆觅处,刚好可以⽤来处理之前遇到的频繁切换标签的问题。今作⼀记录,也好更好的理解这个功能。
述求
点击标签时,取消之前正在执⾏的请求,使得切换标签时,页⾯得到的是最后请求的结果,⽽不是响应最慢的结果。
⽤法
官⽅案例
1. 使⽤ CancelToken.source ⼯⼚⽅法创建 cancel token,像这样:
// CancelToken是⼀个构造函数,⽤于创建⼀个cancelToken实例对象
// cancelToken实例对象包含了⼀个promise属性,值为可以触发取消请求的⼀个promise
const CancelToken = axios.CancelToken;
// 执⾏source()得到的是⼀个包含了cancelToken对象和⼀个取消函数cancel()的对象
// 即 source = {token: cancelToken对象, cancel: 取消函数}
const source = CancelToken.source();
// 在请求的配置中配置cancelToken,那么这个请求就有了可以取消请求的功能
<('/user/12345',{
cancelToken: ken
}).catch(function(thrown){
if(axios.isCancel(thrown)){
console.log('Request canceled', ssage);
}else{
// 处理错误
}
});
axios.post('/user/12345',{
name:'new name'
},{
cancelToken: ken
})
/
/ 执⾏取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
2. 通过传递⼀个 executor 函数到 CancelToken 的构造函数来创建 cancel token
const CancelToken = axios.CancelToken;
let cancel;
<('/user/12345',{
cancelToken:new CancelToken(function executor(c){
// executor 函数接收⼀个 cancel 函数作为参数
// 把cancel函数传递给外⾯,使得外⾯能控制执⾏取消请求
cancel = c;
})
});
// cancel the request
cancel();
看起来有些晕,毕竟不知道⾥⾯是怎么运作的,稍后通过源码解析。这⾥简单解释就是,在请求中配置cancelToken这个属性,是为了使得请求具有可以取消的功能;cancelToken属性的值是⼀个CancelToken实例对象,在它的executor函数中提取出cancel函数,执⾏这
个cancel函数来取消请求。
我的实例
点击标签,执⾏getCourse函数。点击某个标签时,先取消之前的请求(如果之前的请求已完成,取消请求不会有任何操作)。效果是,页⾯显⽰的总是最后点击的标签对应的结果。
分两步,第⼀步,在get请求中配置cancelToken属性,开启取消请求的功能,且在其属性值中将cancel函数赋给cancelRequest,使得外部可以调⽤cancel函数来取消请求;第⼆步,在执⾏请求前,先取消前⼀次的请求。
import axios from'axios'
export default{
data(){
return{
cancelRequest:null// 初始时没有请求需要取消,设为null }
},
methods:{
// 点击标签后发送请求的函数
getCourse(){
const that =this
// 2. 准备执⾏新的请求前,先将前⼀个请求取消
/
/ 如果前⼀个请求执⾏完了,执⾏取消请求不会有其他操作
if(typeof that.cancelRequest ==='function'){
that.cancelRequest()
}
// 这⾥配置请求的参数,略
let params ={}
// 发送请求
<('/api/app/course',{
params: params,
cancelToken:new CancelToken(function executor(c){ // 1. cancel函数赋值给cancelRequest属性
// 从⽽可以通过cancelRequest执⾏取消请求的操作 that.cancelRequest = c
})
})
}
}
}
⼀般API都会统⼀封装,所以,可以将请求封装起来
API
// /api/modules/course.js
// _this为vue组件实例对象
export function getCourseReq(params, _this){
('/api/app/course',{
params: params,
cancelToken:new CancelToken(function executor(c){ // 1. cancel函数赋值给cancelRequest属性
// 从⽽可以通过cancelRequest执⾏取消请求的操作
_this.cancelRequest = c
})
})
.then(res =>{})
.catch(err =>{})
}
组件
import{ getCourseReq }from'@/apis/modules/course'
methods:{
getCourse(){
// 2. 准备执⾏新的请求前,先将前⼀个请求取消
// 如果前⼀个请求执⾏完了,执⾏取消请求不会有其他操作
if(typeof this.cancelRequest ==='function'){
this.cancelRequest()
}
// 这⾥配置请求的参数,略
let params ={}
// 发送请求
getCourseReq(params,this)
.
then(res =>{})
.catch(err =>{})
}
}
遇到的坑
⼀开始按照上述⽅法写好,但请求死活没有取消。⼀遍遍核对了变量名,调试输出信息,啥都对,就是没法取消。折腾半天,想起前⼈配置的axios的interceptor⾥对每个请求配置了cancelToken,⽬的是为了去掉重复的请求,但取消重复请求的代码段被注释掉了。
把cancelToken注释掉之后,终于⾬过天晴。
quest.use(
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]('取消重复请求')
typeof的用法// } else {
requestList.push(request)
return config
},
error =>{
ject(error)
}
)
由于interceptor会在请求发送前做⼀些配置处理,这⾥把原请求中的cancelToken覆盖了,那么即使原请求中执⾏
原cancelToken的cancel函数,由于cancelToken对象不同了,取消操作也就⽆效了。后⾯看了源码可以更明⽩为什么⽆效。
源码解析
根据前⾯的步骤,依次来看看各个源码是怎样。
⾸先,我们为请求配置cancelToken属性,⽬的是使得请求具有能取消请求的功能,它的值是CancelToken实例对象,那么CancelToken是什么呢?
// axios/lib/cancel/CancelToken.js
'use strict';
var Cancel =require('./Cancel');
function CancelToken(executor){
if(typeof executor !=='function'){
throw new TypeError('executor must be a function.');
}
/**
* 定义⼀个将来能执⾏取消请求的promise对象,当这个promise的状态为完成时(fullfilled),
* 就会触发取消请求的操作(执⾏then函数)。⽽执⾏resolve就能将promise的状态置为完成状态。
* 这⾥把resolve赋值给resolvePromise,就是为了在这个promise外能执⾏resolve⽽改变这个promise的状态
* 注意这个promise对象被赋值给CancelToken实例的属性promise,将来定义then函数就是通过这个属性得到promise
*/
var resolvePromise;
this.promise =new Promise(function promiseExecutor(resolve){
resolvePromise = resolve;
});
/**
* 将CancelToken实例赋值给token
* 执⾏executor函数,将cancel⽅法传⼊executor,
* cancel⽅法可调⽤resolvePromise⽅法,即触发取消请求的操作
*/
var token =this;
executor(function cancel(message){
ason){
// 取消已响应返回
return;
}
// 这⾥执⾏的就是promise的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()得到的是⼀个实例对象,它只有⼀个属性promise, 它的值是⼀个能触发取消请求的Promise对象。
token = new CancelToken(executor function) ===> { promise: Promise对象 }
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论