如何通过AbortController.abort的reason参数区分fetch中止场景?
解决Fetch中止时传递自定义中止原因的问题
原生Fetch API在被AbortController.abort()中止时,catch语句里只会收到固定的DOMException('The user aborted a request', 'ABORT_ERROR'),不管你调用abort()时传入什么reason参数,这个错误都不会携带自定义信息。要区分用户主动点击中止和页面跳转两种场景,你可以通过封装Fetch请求的方式,自己关联中止原因:
方法一:标记中止类型后关联到错误
封装一个请求函数,在调用中止方法时标记是用户主动操作还是页面跳转,然后在catch阶段把这个标记附加到错误对象上:
function fetchWithAbortType(url, options = {}) { const controller = new AbortController(); let isUserInitiatedAbort = false; options.signal = controller.signal; const requestPromise = fetch(url, options) .catch(error => { if (error.name === 'AbortError') { error._isUserAbort = isUserInitiatedAbort; } throw error; }); return { promise: requestPromise, abort: (userInitiated = false) => { isUserInitiatedAbort = userInitiated; controller.abort(); } }; }
使用示例
// 发起请求 const { promise, abort } = fetchWithAbortType('/whatever'); // 用户点击中止按钮 document.querySelector('#abort-btn').addEventListener('click', () => { abort(true); // 标记为用户主动中止 }); // 页面跳转时触发中止 function handlePageNavigation() { abort(false); // 标记为页面跳转导致的中止 } // 处理请求结果 promise .then(res => res.json()) .catch(error => { if (error.name === 'AbortError') { if (error._isUserAbort) { // 显示UI提示 alert('call /whatever interrupted'); } // 页面跳转导致的中止则不做UI处理 } else { console.error('请求失败:', error); } });
方法二:捕获AbortSignal的自定义reason
AbortController.abort(reason)传入的参数,会被存在signal.reason里,你可以监听signal的abort事件提前保存这个reason,再在catch阶段附加到错误上:
function fetchWithCustomReason(url, options = {}) { const controller = new AbortController(); let abortReason = null; controller.signal.addEventListener('abort', () => { abortReason = controller.signal.reason; }); options.signal = controller.signal; return { promise: fetch(url, options).catch(error => { if (error.name === 'AbortError' && abortReason) { error.customReason = abortReason; } throw error; }), abort: (reason) => controller.abort(reason) }; }
使用示例
const { promise, abort } = fetchWithCustomReason('/whatever'); // 用户主动中止 document.querySelector('#abort-btn').addEventListener('click', () => { abort('user-interrupted'); }); // 页面跳转中止 function handlePageNavigation() { abort('page-navigation'); } promise .then(res => res.json()) .catch(error => { if (error.name === 'AbortError') { if (error.customReason === 'user-interrupted') { alert('call /whatever interrupted'); } } });
这两种方法都能绕过原生Fetch的限制,让你在catch阶段区分不同的中止场景,按需处理UI提示。
内容的提问来源于stack exchange,提问作者Marco Faustinelli
相关产品推荐
相关产品推荐

