Ajax取消如果你熟悉xhr,你就会知道Ajax其实可以被前端取消,使用XMLHttpRequest.abort()。当然,现在不是刀耕火种的时代。除了面试,你可能基本不会手写xhr。众所周知的axios中有两种取消方法:第一种是老套的cancelToken:constCancelToken=axios.CancelTokenconstsource=CancelToken.source()axios.get('/user/12345',{cancelToken:source.token,}).catch(function(thrown){if(axios.isCancel(thrown)){console.log('Requestcanceled',thrown.message)}else{//处理错误}})axios.post('/user/12345',{name:'newname',},{cancelToken:source.token,})//取消请求(message参数可选)source.cancel('Operationcanceledbytheuser.')然后是新的东西(不是真的新)AbortController:constcontroller=newAbortController()axios.get('/foo/bar',{signal:controller.signal,}).then(function(response){//...})//取消requestcontroller.abort()cancelToken和signal传给axios后,XMLHttpRequest.abort()会被某种机制调用。onCanceled=(cancel)=>{if(!request){return}reject(!cancel||cancel.type?newCanceledError(null,config,request):cancel)request.abort()request=null}config.cancelToken&&config.cancelToken.subscribe(onCanceled)if(config.signal){config.signal.aborted?onCanceled():config.signal.addEventListener('abort',onCanceled)}cancelToken是使用发布-订阅的方式来通知axios取消请求,虽然这部分是axios自己实现的,但是起源于一个tc39的proposalcancelablepromises提案,但是这个提案被放弃了。而AbortController是一个已经可以在浏览器中使用的接口。顾名思义,这是一个专用于中止行为的控制器。mdn的例子也是用了一个Ajax请求,不过是从潮到里的fetch。可见axios和fetch的做法是一致的:functionfetchVideo(){controller=newAbortController()//新建一个controllerconst信号=controller.signalfetch(url,{signal})//在fetch方法中传递信号.then(function(response){console.log('Downloadcomplete',response)}).catch(function(e){console.log('下载错误:'+e.message)})}abortBtn.addEventListener('click',function(){if(controller)controller.abort()//调用controller.abort取消获取console.log('Downloadaborted')})AbortController的其他用法当然,AbortController不仅仅是一个abortAjax的功能,查看dom规范文档还可以看到两个用法示例:一个比较实用的例子是使用AbortController取消事件监听:字典AddEventListenerOptions:EventListenerOptions{布尔被动=false;布尔一次=假;AbortSignal信号;};通过将信号传递给AddEventListener,运行abort()来取消事件监听。此方法对于匿名回调函数特别有用。另一个例子是违背承诺。这是一个比较简洁的自文档化的方法。。。但是其实并不需要AbortController来实现这个功能,只要想办法拿到promise的rejection就可以了。我认为这个例子的重点是学习使用signalonabort:constcontroller=newAbortController();常量信号=controller.signal;启动微调器();doAmazingness({...,signal}).then(result=>...).catch(err=>{if(err.name=='AbortError')return;showUserErrorMessage();}).then(()=>stopSpinner());//...controller.abort();functiondoAmazingness({signal}){returnnewPromise((resolve,reject)=>{signal.throwIfAborted();//开始做令人惊奇的事情,完成后调用resolve(result)。//同时,注意信号:signal.addEventListener('abort',()=>{//别再惊奇了,and:reject(signal.reason);});});}总之,signal是一个简单的发送者,功能偏向于取消操作。如果你不想在特定情况下自己实现一个pubsub对象,使用它就可以了。AbortController的介绍到此结束。不知道大家是不是渐渐忘记了标题……最后想讨论一下取消Ajax有没有用?取消还是不取消,这是一个问题。事实上,这个Ajax取消只是前端在自言自语。后端不知道中止。过去发送的请求仍然需要执行。如果后端没有特殊处理,可以取消10s请求。后端也还在苦苦挣扎。那么在某些文章中看到的“优化”,所谓的“取消请求,只保留最后一个”真的有意义吗?分情况讨论,对于POST等修改数据的请求,即使每次返回很慢,服务器也已经在处理了。取消之前的POST重新发送,无疑是一种弱智行为。对于GET,并且只针对一些极端的操作,可能会有一点影响,例如:获取一个超长的表,但是没有获取到结果,然后用户用search快速返回少量数据并渲染,并且会等到超长表真正返回时才会覆盖搜索到的数据,这种情况下cancel才真正有效。还有取消下载和上传,不过估计很少用到。最后,还有一个说的通但实际上没用的好处:cancel后可以保存一个请求位置。毕竟浏览器同时请求域名的次数是有限的。更多情况下,比cancel更常见的timeout更实用。嗯……除非队列中同时有五六个超慢的请求,否则轮换还是比较快的……个人建议毕竟这个所谓的“取消”是一种特殊待遇一个非常特殊的情况。很高兴知道它。不需要在拦截器中取消整个操作。参考domspecGitHubaxiosmdnAbortController
