当前位置: 首页 > Web前端 > HTML

axios拦截重复请求(请求未完成时取消后面发起的同一个请求)

时间:2023-04-03 01:08:02 HTML

背景最近项目有个奇怪的问题。项目完成后会交给甲方,因为甲方的数据库比我们的模拟图书馆大。体积要大很多(单表有上亿的体积)。直接导致接口请求的时间超过30s。..当然,数据优化只能交给后端同事。但目前最重要的工作是提供一个可见的前端页面供甲方验收。请求慢的问题暴露了前端的很多缺陷。补充加载提示等功能后,用户大量重复点击产生的重复界面请求,会导致请求结束后同样的数据大量涌入。在网上找了一些博主的方法(见文章未链接),都是拦截了之前的请求。比如发送了一个请求a,在a完成之前,又发送了一个与a相同的请求a2。博主的方法是取消请求a,只保留a2及以上。当接口快的时候,就没有问题了。但是当接口慢的时候,接口a的请求快完成了,此时给它一个cancel。有点不合理。所以我现在做的就是取消后面的a2,保留a。开始爬axios官方文档过关门。取消正在发送或已经发送但还没有返回数据的请求,使用axios.CancelToken对象中的方法,如下:第一种方法:使用CancelToken。源工厂方法创建取消令牌,如下所示:constCancelToken=axios.CancelToken;constsource=CancelToken.source();axios.get('/user/12345',{cancelToken:source.token}).catch(function(thrown){if(axios.isCancel(thrown)){console.log('请求取消',thrown.message);}else{//处理错误}});axios.post('/user/12345',{name:'newname'},{cancelToken:source.token})//取消请求(message参数可选)source.cancel('操作被用户取消.');第二种方法:通过将Createacanceltoken从executor函数传递给CancelToken构造函数:constCancelToken=axios.CancelToken;letcancel;axios.get('/user/12345',{cancelToken:newCancelToken(functionexecutor(c){//执行函数接收一个取消函数作为参数cancel=c;})});//取消requestcancel();两种方法的区别和应用第一种方法是多个请求共享一个token,适用于在某个时间取消所有请求,第二种方法每个请求有一个独立的token,适合每个请求到视情况分别取消。显然,这个需求需要用到第二种方法。实现的核心方法是在请求时将请求信息存储在一个数组中,请求完成后从数组中取出。下次发送请求时,检查数组中是否存在相同的请求,如果存在则取消本次请求。参考axios文档,请求拦截和响应拦截都会返回请求的原始数据。//添加请求拦截器//config中有url、data、params等信息axios.interceptors.request.use(function(config){returnconfig;});//添加响应拦截器//其中response.config和请求拦截的配置是相同的axios.interceptors.response.use(function(response){returnresponse;});封装方法constCancelToken=axios.CancelTokenletrequestQueue=[]//请求拦截调用函数handleRequest({config}){//提取四个参数区分同一个请求const{url,method,data={},params={}}=配置;constjData=JSON.stringify(data),jParams=JSON.stringify(params)constpanding=requestQueue.filter(item=>{returnitem.url===url&&item.method===方法&&item.data===jData&&item.params===jParams})if(panding.length){//这里是重点。实例化CancelToken时,立即调用参数c,立即取消当前请求config.cancelToken=newCancelToken(c=>c(`重复请求主动拦截:${url}+${jData}+${jParams}`))}else{//如果请求不存在,则将数据转成JSON字符串格式,方便后面的对比requestQueue.push({url,data:jData,params:jParams,method,})}}//响应拦截调用函数handleResponse({config}){const{url,data=JSON.stringify({}),params=JSON.stringify({})}=configletreqQueue=requestQueue.filter(item=>{returnitem.url!==url&&item.data!==data&&item.params!==params})requestQueue=reqQueue}在请求封装中调用//请求拦截axios.interceptors.request.use(function(config){handleRequest({config})returnconfig;});//响应拦截器axios.interceptors.response.use(function(response){handleResponse({config:response.config})returnresponse;});测试运行结果总结以上方法使用了axios的CancelToken方法。事实上,文档并没有具体说明什么时候可以调用这个方法,这导致很多开发者认为只有在panding状态太低的时候才能使用。经过我的测试,可以在请求拦截中调用cancel上述方法存在的问题:使用JSON.stringify处理对象,在对象上使用JSON方法,key-value排列会乱。您可以先调用sort()方法进行排序。由于我在项目中有很多接口细分,请求参数不多,忽略这个缺陷。如果在请求拦截中不返回config,是不是就不发送请求了?经测试,如果在请求拦截中youdonotreturnorireturnconfig,axios会报错。虽然拦截了个别接口,但是控制台并没有打印消息Cancel{message:'url'}。找不到原因。在我写博文时,这个改动还没有通过生产检验。欢迎大家帮我找bug。参考:axios的二次封装(拦截重复请求,统一处理异常)axios取消请求和防止重复请求的方法