在日常开发中一般只会在表单提交这种接口做一下防抖,但是最近公司的项目服务器压力大得很,接口越来越慢,所以需要对于重复接口进行取消,减轻一下服务器压力,就有了这个文章。
官方提供的方式
在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) { 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()
function generateKey4Request(config){ const {method, url, params, data} = config return [method, url, qs.stringify(params), qs.stringify(data)].join("&") }
function collectRequest(config){ const key = generateKey4Request(config) config.cancelToken = config.cancelToken || new axios.CancelToken(c => { if(!pendingRequestMap.has(key)){ pendingRequestMap.set(key, c) } }) }
function checkRequest(config){ const key = generateKey4Request(config) if(pendingRequestMap.has(key)){ 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); collectRequest(config); return config; }, (error) => { return Promise.reject(error); } );
axios.interceptors.response.use( (response) => { checkRequest(response.config); return response; }, (error) => { 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);
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
return instance; }
var axios = createInstance(defaults);
axios.Axios = Axios;
axios.create = function create(instanceConfig) { return createInstance(utils.merge(defaults, instanceConfig)); };
axios.Cancel = require('./cancel/Cancel'); axios.CancelToken = require('./cancel/CancelToken'); axios.isCancel = require('./cancel/isCancel');
axios.all = function all(promises) { return Promise.all(promises); };
|
可见,Cancel,CancelToken这些方法是后边加到axios上去的,而通过axios.create创建的实例并没有添加这些方法,如果需要在axios.create创建的实例上使用Cancel方法,需要额外处理。
由于本人没有很优雅的实现方式,在此不表
2022年10月9日更新,又在看axios的源码,想到了一种可能。axios就是把Axios类的一些方法直接复制过来的,那么我把axios的Cancel等方法也复制到axios.create创建的实例身上是否可行呢?