口试者:(脑海里立马产生一个迷惑:已经发出去的要求还能取消掉?) 这个......这个......还真不知道。
口试完,立时找度娘.....
AbortController
AbortController[1] 接口表示一个掌握器工具,可以根据须要终止一个或多个Web要求。

Fetch 是 Web 供应的一个用于获取资源的接口,如果要终止 fetch 要求,则可以利用 Web 供应的 AbortController 接口。
首先我们利用 AbortController() 布局函数创建一个掌握器,然后利用 AbortController.signal 属性获取其关联 AbortSignal 工具的引用。
当一个 fetch request 初始化时,我们把 AbortSignal 作为一个选项通报到要求工具 (如下:{signal}) 。这将旗子暗记和掌握器与获取要求干系联,然后许可我们通过调用 AbortController.abort() 中止要求。
const controller = new AbortController();let signal = controller.signal; console.log('signal 的初始状态: ', signal);const downloadBtn = document.querySelector('.download');const abortBtn = document.querySelector('.abort');downloadBtn.addEventListener('click', fetchVideo);abortBtn.addEventListener('click', function() { controller.abort(); console.log('signal 的中止状态: ', signal);});function fetchVideo() { //... fetch(url, {signal}).then(function(response) { //... }).catch(function(e) { reports.textContent = 'Download error: ' + e.message; })}
当我们中止要求后,网络要求变成了如下所示的情形:
我们再来看看 AbortSignal 中止前和中止后的状态:
可以看到,AbortSignal 工具的 aborted 属性由初始时的 false 变成了中止后的 true 。
线上运行示例[2] (代码来源于MDN[3])
AbortControllter 有兼容性问题,如下:
axios 中断要求
axions 中断要求有两种办法:
办法一
利用 CancelToken.souce 工厂方法创建一个 cancel token,代码如下:
const CancelToken = axios.CancelToken;const source = CancelToken.source();axios.get('https://mdn.github.io/dom-examples/abort-api/sintel.mp4', { cancelToken: source.token}).catch(function (thrown) { // 判断要求是否已中止 if (axios.isCancel(thrown)) { // 参数 thrown 是自定义的信息 console.log('Request canceled', thrown.message); } else { // 处理缺点 }});// 取消要求(message 参数是可选的)source.cancel('Operation canceled by the user.');
中止后的网络要求变成如下所示:
我们再来看看初始时和中止后的 souce 状态:
可以看到,初始时和中止后的 source 状态并没还有发生改变。那么我们是如何判断要求的中止状态呢?axios 为我们供应了一个 isCancel() 方法,用于判断要求的中止状态。isCancel() 方法的参数,便是我们在中止要求时自定义的信息。
办法二
通过通报一个 executor 函数到 CancelToken 的布局函数来创建一个 cancel token:
const CancelToken = axios.CancelToken;let cancel;axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函数吸收一个 cancel 函数作为参数 cancel = c; })});// 取消要求cancel('Operation canceled by the user.');
浏览器运行结果与办法逐一致,此处不再赘述。
线上运行示例[4] (代码来源于MDN[3])
umi-request 中断要求umi-request 基于 fetch 封装, 兼具 fetch 与 axios 的特点, 中止要求与 fetch 和 axios 同等不再过多赘述,详情可见官方文档 中止要求[5]
须要把稳的是 AbortController 在低版本浏览器polyfill有问题,umi-request 在某些版本中并未供应 AbortController 的办法中止要求。
umi 项目中利用 CancelToken 中止要求umi 项目中默认的要求库是umi-request,因此我们可以利用umi-request供应的方法来中止要求。其余,在umi项目中可以搭配利用了dva,因此下面大略先容下在dva中利用CancelToken中止要求的流程。
1、在 services 目录下的文件中编写要求函数和取消要求的函数
import request from '@/utils/request';const CancelToken = request.CancelToken;let cancel: any;// 条约文件上传 OSSexport async function uploadContractFileToOSS(postBody: Blob): Promise<any> { return request(`/fms/ossUpload/financial_sys/contractFile`, { method: "POST", data: postBody, requestType: 'form', // 通报一个 executor 函数到 CancelToken 的布局函数来创建一个 cancel token cancelToken: new CancelToken((c) => { cancel = c }) })}// 取消条约文件上传export async function cancelUploadFile() { return cancel && cancel()}
2、在 models 中编写 Effect:
uploadContractFileToOSS({ payload }: AnyAction, { call, put }: EffectsCommandMap): any { const response = yield call(uploadContractFileToOSS, payload); yield put({ type: 'save', payload: { uploadOSSResult: response?.data, } }) return response?.data},cancelUploadFile(_: AnyAction, { call }: EffectsCommandMap): any { const response = yield call(cancelUploadFile) return response},
3、在页面中通过dispatch函数触发相应的action:
// 发起要求dispatch({ type: 'contract/fetchContractFiles', payload: { contractId: `${id}`, }})// 取消要求dispatch({ type: "contract/cancelUploadFile"})
4、在 utils/request.js 中统一处理中止要求的拦截:
const errorHandler = (error: { response: Response }): Response => { const { response } = error; notification.destroy() if (response && response.status) { const errorText = codeMessage[response.status] || response.statusText; const { status, url } = response; notification.error({ message: `要求缺点 ${status}: ${url}`, description: errorText, }); } else if (error?.['type'] === 'TypeError') { notification.error({ description: '您的网络发生非常,无法连接做事器', message: '网络非常', }); } else if (error?.['request']?.['options']?.['cancelToken']) { notification.warn({ description: '当前要求已被取消', message: '取消要求', }); } else if (!response) { notification.error({ description: '您的网络发生非常,无法连接做事器', message: '网络非常', }); } else { notification.error({ description: '请联系网站开拓职员处理', message: '未知缺点', }); } return response;};
作者:紫圣
https://juejin.cn/post/7033906910583586829