axios取消请求

在日常开发中一般只会在表单提交这种接口做一下防抖,但是最近公司的项目服务器压力大得很,接口越来越慢,所以需要对于重复接口进行取消,减轻一下服务器压力,就有了这个文章。

官方提供的方式

在axios0.22版本以上,官方提供了新的取消请求的方式,但由于公司项目用的是axios0.18,所以还在用老办法取消请求。对于老版本官方提供了两种取消请求的方式:

第一种方式
1
2
3
4
5
6
7
8
9
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('xx', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求 (消息参数是可选的)
source.cancel('请勿重复提交');
第二种方式
1
2
3
4
5
6
7
8
9
10
const CancelToken = axios.CancelToken;
let cancel;
axios.post('xx', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel();

封装

一般来说我们都会对axios请求进行封装,设置请求和响应拦截器。

所以可以在请求拦截器中收集新请求,取消重复请求。在响应拦截器中取消对于已完成请求的收集

先说一下思路吧:

  • 我们在这里将请求方式、路径和参数都一致的请求判定为重复请求。
  • 通过Map收集请求提高查找效率。
  • 在拦截器中做相应处理。
请求的收集和移除

定义几个函数来辅助计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import qs from 'querystring'
const pendingRequestMap = new Map()
// 根据请求路径和参数生成请求的key
function generateKey4Request(config){
const {method, url, params, data} = config
return [method, url, qs.stringify(params), qs.stringify(data)].join("&")
}

// 收集未完成的请求
function collectRequest(config){
// 根据请求配置生成key
const key = generateKey4Request(config)
// 设置cancelToken
config.cancelToken = config.cancelToken || new axios.CancelToken(c => {
// 如果当前key不存在,则收集进去
if(!pendingRequestMap.has(key)){
pendingRequestMap.set(key, c)
}
})
}

// 检查重复请求并取消
function checkRequest(config){
const key = generateKey4Request(config)
// 如果这个key存在,也就是存在重复请求,那么就取消这个请求并将其从map中移除
if(pendingRequestMap.has(key)){
// 得到cancelToken
const cancelToken = pendingRequestMap.get(key)
// 取消请求
cancelToken(key)
pendingRequestMap.delete(key)
}
}
拦截器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 请求拦截器
axios.interceptors.request.use(
function (config) {
// 检查是否存在重复请求
checkRequest(config);
// 将当前请求信息添加到 pendingRequestMap中
collectRequest(config);
return config;
},
(error) => {
return Promise.reject(error);
}
);

// 响应拦截器
axios.interceptors.response.use(
(response) => {
// 请求完成了则从 pendingRequestMap中移除请求
checkRequest(response.config);
return response;
},
(error) => {
// 出错了该请求也已经结束,也要从 pendingRequestMap中移除请求
checkRequest(error.config || {});
if(axios.isCancel(error)){
console.log(error.message);
}else {
// 自行处理异常请求
}
return Promise.reject(error);
}
);

注意

我们经常会使用axios.create()创建实例来使用,但是需要注意的是,通过axios.create()创建的实例身上没有Cancel,CancelToken,all这些方法。

贴一段源码大家就知道为什么没有这些方法了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);

// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);

// Copy context to instance
utils.extend(instance, context);

return instance;
}

// Create the default instance to be exported
var axios = createInstance(defaults);

// Expose Axios class to allow class inheritance
axios.Axios = Axios;

// Factory for creating new instances
axios.create = function create(instanceConfig) {
return createInstance(utils.merge(defaults, instanceConfig));
};

// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');

// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};

可见,Cancel,CancelToken这些方法是后边加到axios上去的,而通过axios.create创建的实例并没有添加这些方法,如果需要在axios.create创建的实例上使用Cancel方法,需要额外处理。

由于本人没有很优雅的实现方式,在此不表

2022年10月9日更新,又在看axios的源码,想到了一种可能。axios就是把Axios类的一些方法直接复制过来的,那么我把axiosCancel等方法也复制到axios.create创建的实例身上是否可行呢?

作者

胡兆磊

发布于

2022-06-29

更新于

2022-10-23

许可协议