我正在实现可中止的获取调用。
在我的页面上中止提取基本上有两个原因:
为了区分这两种情况,我计划使用
reason
方法的 AbortController.abort
参数,但 fetch 调用中的 .catch 子句总是收到 DOMException('The user aborted a request', ABORT_ERROR)
。
DOMException
作为情况 2 中止的原因,但差异丢失了。
有没有人找到如何将有关中止原因的信息发送到 fetch .catch 子句?
在下面的示例中,我演示了如何确定
fetch
请求中止的原因。我提供内嵌注释以进行解释。如果有什么不清楚的地方请随时评论。
重新运行代码片段以查看(可能不同的)随机结果
'use strict';
function delay (ms, value) {
return new Promise(res => setTimeout(() => res(value), ms));
}
function getRandomInt (min = 0, max = 1) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Forward the AbortSignal to fetch:
// https://docs.github.com/en/rest/repos/repos#list-public-repositories
function fetchPublicGHRepos (signal) {
const headers = new Headers([['accept', 'application/vnd.github+json']]);
return fetch('https://api.github.com/repositories', {headers, signal});
}
function example () {
const ac = new AbortController();
const {signal} = ac;
const abortWithReason = (reason) => delay(getRandomInt(1, 5))
.then(() => {
console.log(`Aborting ${signal.aborted ? 'again ' : ''}(reason: ${reason})`);
ac.abort(reason);
});
// Unless GitHub invests HEAVILY into our internet infrastructure,
// one of these promises will resolve before the fetch request
abortWithReason('Reason A');
abortWithReason('Reason B');
fetchPublicGHRepos(signal)
.then(res => console.log(`Fetch succeeded with status: ${res.status}`))
.catch(ex => {
// This is how you can determine if the exception was due to abortion
if (signal.aborted) {
// This is set by the promise which resolved first
// and caused the fetch to abort
const {reason} = signal;
// Use it to guide your logic...
console.log(`Fetch aborted with reason: ${reason}`);
}
else console.log(`Fetch failed with exception: ${ex}`);
});
delay(10).then(() => console.log(`Signal reason: ${signal.reason}`));
}
example();
中止原因可通过
reason
属性在信号中获得。如果您自己设置信号,就很容易访问。
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
因此,当
fetch
抛出错误时,如果它是带有 DOMException
名称的 'AbortError'
,您可以依赖信号的原因。
const controller = new AbortController();
setTimeout(() => controller.abort('Timeout'), 30000);
try {
const response = await fetch('some_url', { signal: controller.signal });
console.log('Fetch response:', response);
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
console.warn('Fetch aborted:', signal.reason);
} else {
console.error('Fetch error:', error);
}
}
}
您还可以创建自己的获取函数来获取正确的异常:
async function customFetch(url, params) {
try {
return fetch(url, params);
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
// params.signal should be defined at this point,
// but using the nullish coalesce operators anyway is safer
params?.signal?.throwIfAborted();
}
// Rethrow at catch level in the unlikely case params.signal is undefined
throw error;
}
}
通过使用此自定义函数,
fetch
函数在中止时产生的异常将替换为AbortSignal
异常:
const controller = new AbortController();
setTimeout(() => controller.abort('Timeout'), 30000);
try {
const response = await customFetch('some_url', { signal: controller.signal });
console.log('Fetch response:', response);
} catch (error) {
// Now when the request is aborted, the reason is thrown as a plain string
if (error === 'Timeout') {
console.warn('Fetch timeout');
} else {
console.error('Fetch error:', error);
}
}
}
请注意,在
throwIfAborted
函数中调用信号的 customFetch
可确保抛出的异常是标准的(中止原因为纯字符串),但您也可以抛出自己的异常。