当前位置: 首页 > 科技观察

说说axios如何取消重复请求?

时间:2023-03-22 15:41:07 科技观察

Web项目开发过程中,经常会遇到重复请求的场景。如果系统不处理重复的请求,可能会导致系统出现各种问题。例如,重复的post请求可能会导致服务器生成两条记录。那么重复请求是如何发生的呢?这里我们举两个常见的场景:假设页面上有一个按钮,用户点击按钮后会发起AJAX请求。如果按钮不受控制,当用户快速点击按钮时,会重复请求。假设在测试结果查询页面,用户可以根据“通过”、“未通过”和“全部”三种查询条件查询测试结果。如果请求的响应较慢,当用户在不同的查询条件之间快速切换时,就会产生重复的请求。现在您知道了重复请求是如何产生的,您也知道它会导致一些问题。接下来阿宝哥就以axios为例,带大家解决重复请求的问题。一、如何取消请求Axios是一个基于Promise的HTTP客户端,同时支持浏览器和Node.js环境。它是一个优秀的HTTP客户端,被广泛应用于大量的Web项目中。对于浏览器环境,Axios底层使用XMLHttpRequest对象发起HTTP请求。如果我们想取消请求,可以通过调用XMLHttpRequest对象的abort方法来取消请求:letxhr=newXMLHttpRequest();xhr.open("GET","https://developer.mozilla.org/",真的);xhr.send();setTimeout(()=>xhr.abort(),300);对于axios,我们可以通过axios内部提供的CancelToken取消请求:constCancelToken=axios.CancelToken;constsource=CancelToken.source();axios.post('/user/12345',{name:'semlinker'},{cancelToken:source.token})source.cancel('Operationcanceledbytheuser.');//取消请求,参数可选另外,也可以通过调用CancelToken的构造函数来创建一个CancelToken,如下:constCancelToken=axios.CancelToken;letcancel;axios.get('/user/12345',{cancelToken:newCancelToken(functionexecutor(c){cancel=c;})});cancel();//取消请求现在我们知道如何使用CancelToken来取消一个Axios中的request,CancelToken内部是如何工作的?这里先记下这个问题,稍后包哥为大家揭秘CancelToken背后的秘密。接下来我们分析一下如何判断重复请求。2、如何判断重复请求当请求方式、请求URL地址、请求参数都相同时,我们可以认为请求是相同的。因此,每次发起请求,我们可以根据当前的请求方式、请求URL地址、请求参数生成一个唯一的key,并为每个请求创建一个专用的CancelToken,然后将key和cancel函数作为key的值对存储在Map对象中。使用Map的好处是可以快速判断是否有重复请求:importqsfrom'qs'constpendingRequest=newMap();//GET->params;POST->dataconstrequestKey=[method,url,qs.stringify(params),qs.stringify(data)].join('&');constcancelToken=newCancelToken(functionexecutor(cancel){if(!pendingRequest.has(requestKey)){pendingRequest.set(requestKey,cancel);}})当有是一个重复的请求,我们可以使用cancel函数来取消之前的请求。取消请求后,我们需要将取消的请求从pendingRequest中移除。知道了如何取消请求以及如何判断重复请求,下面我们来介绍一下如何取消重复请求。3、如何取消重复请求因为我们需要处理所有的请求,所以可以考虑使用axios的拦截器机制来实现取消重复请求的功能。Axios为开发者提供了请求拦截器和响应拦截器。它们的作用如下:请求拦截器:这类拦截器的作用是在请求发送前统一执行某些操作,比如在请求头中添加token字段。响应拦截器:这类拦截器的作用是在收到服务器响应后统一执行某些操作,比如发现响应状态码为401时自动跳转到登录页面。3.1定义辅助函数在配置请求拦截器和响应拦截器之前,宝哥首先定义了三个辅助函数:generateReqKey:用于根据当前请求的信息生成请求Key;functiongenerateReqKey(config){const{method,url,params,data}=config;return[method,url,Qs.stringify(params),Qs.stringify(data)].join("&");}addPendingRequest:使用将当前请求信息添加到pendingRequest对象中;constpendingRequest=newMap();functionaddPendingRequest(config){constrequestKey=generateReqKey(config);config.cancelToken=config.cancelToken||newaxios.CancelToken((cancel)=>{if(!pendingRequest.has(requestKey)){pendingRequest.set(requestKey,cancel);}});}removePendingRequest:检查是否有重复请求,如果有则取消发送的请求。functionremovePendingRequest(config){constrequestKey=generateReqKey(config);if(pendingRequest.has(requestKey)){constcancelToken=pendingRequest.get(requestKey);cancelToken(requestKey);pendingRequest.delete(requestKey);}}创建generateReqKey,addPendingRequest在removePendingRequest函数之后,我们可以设置请求拦截器和响应拦截器。3.2设置请求拦截器axios.interceptors.request.use(function(config){removePendingRequest(config);//检查是否有重复请求,如果有则取消发送的请求addPendingRequest(config);//把当前请求信息加入pendingRequest对象returnconfig;},(error)=>{returnPromise.reject(error);});3.3设置响应拦截器axios.interceptors.response.use((response)=>{removePendingRequest(response.config);//从pendingRequest对象中移除请求returnresponse;},(error)=>{removePendingRequest(error.config||{});//从pendingRequest对象中移除请求if(axios.isCancel(error)){console.log("取消重复请求:"+error.message);}else{//添加异常处理}returnPromise.reject(error);});由于完整示例代码内容较多,阿宝哥就不放具体代码了。感兴趣的小伙伴,可以访问以下地址浏览示例代码。完整示例代码:https://gist.github.com/semlinker/e426780664f0186db434882f1e27ac3a下面我们看axios取消重复请求示例的运行结果:从上图我们可以看到,当出现重复请求时,之前发送的而未完成的请求将被取消。下面用一张流程图总结一下取消重复请求的处理流程:最后,我们来回答一下上面留下的问题,即CancelToken内部是如何工作的?4.CancelToken的工作原理在前面的例子中,我们是通过调用CancelToken的构造函数创建一个CancelToken对象:newaxios.CancelToken((cancel)=>{if(!pendingRequest.has(requestKey)){pendingRequest.set(requestKey,cancel);}})那么接下来我们分析一下CancelToken的构造函数,它定义在lib/cancel/CancelToken.js文件中://lib/cancel/CancelToken.jsfunctionCancelToken(executor){if(typeofexecutor!=='function'){thrownewTypeError('executormustbeafunction.');}varresolvePromise;this.promise=newPromise(functionpromiseExecutor(resolve){resolvePromise=resolve;});vartoken=this;executor(functioncancel(message){//设置取消对象if(token.reason){return;//Cancellationhasalreadybeenrequested}token.reason=newCancel(message);resolvePromise(token.reason);});}从上面的代码我们可以看出cancel对象是一个函数。当我们调用此函数时,将创建Cancel对象并调用resolvePromise方法。执行此方法后,CancelToken对象上的promise属性指向的promise对象的状态将变为已解决。那么这样做的目的是什么?在这里我们从lib/adapters/xhr.js文件中找到了答案://lib/adapters/xhr.jsif(config.cancelToken){config.cancelToken.promise.then(functiononCanceled(cancel){if(!request){return;}request.abort();//取消请求reject(cancel);request=null;});}看了上面的内容,可能有些朋友不是很能理解CancelToken的工作原理,所以阿宝哥再画一张图帮助大家理解CancelToken的工作原理:五、小结本文介绍了axios中如何取消重复请求以及CancelToken的工作原理。在后续的文章中,阿宝哥会介绍如何在axios中设置数据缓存,感兴趣的朋友不要错过。如果想了解Axios中HTTP拦截器和HTTP适配器的设计与实现,可以阅读77.9KAxios项目值得学习的文章这篇文章。6.参考资源Github-AxiosMDN-XMLHttpRequest77.9K有哪些值得学习的axios项目?