面试官:请求已发送,如何取消已发送的请求?面试官:(脑子里立马冒出一个疑问:已经发出的请求能取消吗?)这……这……我真不知道。面试完马上找度娘...推荐阅读:axios解析之cancelToken取消请求原理[2]AbortControllerAbortController[3]该接口代表一个controller对象,可以根据需要终止一个或多个web请求。AbortController():AbortController()构造函数创建一个新的AbortController对象实例signal:signal属性返回一个AbortSignal对象实例,它可以与/关于Web(网络)请求一起使用abort():终止一个未完成的Web(网络)请求,它可以终止fetch请求,任何响应Body和streamFetch中断请求的消费者Fetch是Web提供的获取资源的接口,如果要终止fetch请求,可以使用Web提供的AbortController接口.首先,我们使用AbortController()构造函数创建一个控制器,然后使用AbortController.signal属性获取对其关联的AbortSignal对象的引用。初始化获取请求时,我们将AbortSignal作为选项传递给请求对象(以下:{signal})。这将信号和控制器与get请求相关联,然后允许我们通过调用AbortController.abort()来中止请求。constcontroller=newAbortController();letsignal=controller.signal;console.log('信号初始状态:',signal);constdownloadBtn=document.querySelector('.download');constabortBtn=document.querySelector('.abort');down??loadBtn.addEventListener('click',fetchVideo);abortBtn.addEventListener('click',function(){controller.abort();console.log('signalabortstatus:',signal);});functionfetchVideo(){//...fetch(url,{signal}).then(function(response){//...}).catch(function(e){reports.textContent='Downloaderror:'+e.message;})}复制代码当我们中止请求时,网络请求就变成了这样:我们来看看AbortSignal中止前后的状态:可以看到AbortSignal对象的aborted属性由false在abort后beginning变为true。在线运行示例[4](代码来自MDN[5])AbortControllter存在兼容性问题,如下:axios中断请求axions中断请求有两种方式:方法1使用CancelToken.souce工厂方法创建取消令牌,代码如下:constCancelToken=axios.CancelToken;constsource=CancelToken.source();axios.get('https://mdn.github.io/dom-examples/abort-api/sintel.mp4',{cancelToken:source.token}).catch(function(thrown){//判断请求是否已经终止if(axios.isCancel(thrown)){//抛出的参数为自定义消息console.log('Requestcanceled',thrown.message);}else{//处理错误}});//取消请求(message参数可选)source.cancel('操作被用户取消。');复制代码中止后的网络请求变成如下:我们再看一下初始时间和终止后源的状态:可以看出源在开始和终止后的状态都没有变化.那么我们如何判断请求的挂起状态呢?axios为我们提供了一个isCancel()方法来判断请求的暂停状态。isCancel()方法的参数是我们自定义的中止请求时的信息。方法2通过向CancelToken构造函数传递一个executor函数来创建一个取消令牌:constCancelToken=axios.CancelToken;letcancel;axios.get('/user/12345',{cancelToken:newCancelToken(functionexecutor(c){//executor函数接收一个取消函数作为参数ccancel=c;})});//取消请求cancel('Operationcancelledbytheuser.');复制代码浏览器运行结果与方法一一致,这里不再赘述。在线运行示例[6](代码来自MDN[7])umi-request中断请求umi-request是基于fetch封装的,兼有fetch和axios的特点。abortrequest与fetch和axios一致,不再详述可见官方文档abortsrequest[8]。需要注意的是,AbortController对于低版本浏览器的polyfill存在问题,umi-request在部分版本中没有提供AbortController来中止请求。umi项目使用CancelToken中止请求umi项目中默认的请求库是umi-request,所以我们可以使用umi-request提供的方法中止请求。另外,在umi项目中dva是可以一起使用的,所以下面简单介绍一下在dva中使用CancelToken中止请求的过程。1.在services目录下的文件中写请求函数和取消请求函数any>{returnrequest(`/fms/ossUpload/financial_sys/contractFile`,{method:"POST",data:postBody,requestType:'form',//传递一个executor函数给CancelToken的构造函数,创建一个canceltokencancelToken:newCancelToken((c)=>{ccancel=c})})}//取消合约文件上传exportasyncfunctioncancelUploadFile(){returncancel&&cancel()}复制代码2.在模型中写入Effect:*uploadContractFileToOSS({payload}:AnyAction,{call,put}:EffectsCommandMap):any{constresponse=yieldcall(uploadContractFileToOSS,payload);yieldput({type:'save',payload:{uploadOSSResult:response?.data,}})returnresponse?.data},*cancelUploadFile(_:AnyAction,{call}:EffectsCommandMap):any{constresponse=yieldcall(cancelUploadFile)returnresponse},复制code3.通过页面的dispatch函数触发相应的action://发起请求dispatch({type:'contract/fetchContractFiles',payload:{contractId:`${id}`,}})//取消请求dispatch({type:"contract/cancelUploadFile"})复制代码4.统一在utils/request.js中拦截abort请求:consterrorHandler=(错误:{response:Response}):Response=>{const{response}=error;notification.destroy()if(response&&response.status){conterrorText=codeMessage[response.status]||response.statusText;const{status,url}=response;notification.error({message:`请求错误${status}:${url}`,description:errorText,});}elseif(error?.['type']==='TypeError'){notification.error({description:'您的网络出现异常,无法连接到服务器',message:'网络异常',});}elseif(error?.['request']?.['options']?.['cancelToken']){notification.warn({description:'当前请求已被取消',message:'取消请求',});}elseif(!response){notification.error({description:'你网络出现异常,无法连接服务器',message:'网络异常',});}else{notification.error({description:'请联系网站开发者处理',message:'未知错误',});}returnresponse;};复制代码
