I have following code:
const getData = async function () {
try {
const response = await fetch(`${API_URL}/cars`, {
signal: AbortSignal.timeout(5_000),
});
console.log(response);
} catch (err) {
console.log(err.name); //AbortError
console.dir(err);
}
};
getData();
Here, getData uses fetch()
to retrieve data from an API endpoint, I set signal
property of request to AbortSignal.timeout(5_000)
to implement timeout.
According to MDN documentation, AbortSignal.timeout(TIME_IN_MS)
will return AbortSignal
that will automatically abort after specified time. The signal aborts with either TimeoutError
on timeout, or with AbortError
due to pressing a browser stop button, closing the tab (or some other inbuilt "stop" operation).
The response that I get every time when running getData()
is AbortError
with message The user aborted a request.
and never TimeoutError
, even when I set browser to throttle to slow 3G and set time to 100ms. I also tried setting the response to take 10s in the server and in the front end to no throttle and timeout to 9000ms.
Why don't I get TimeoutError
but only get AbortError
?
Update 1:
This issue seems to only be for chromium-based browsers like Google Chrome, Microsoft Edge but works as Expected in Firefox.
Update 2:
As pointed by @Kaiido, this seems to be error of not only Chromium but also Safari. Issue is opened on both:
- Chromium:
- Safari: .cgi?id=246069
I have following code:
const getData = async function () {
try {
const response = await fetch(`${API_URL}/cars`, {
signal: AbortSignal.timeout(5_000),
});
console.log(response);
} catch (err) {
console.log(err.name); //AbortError
console.dir(err);
}
};
getData();
Here, getData uses fetch()
to retrieve data from an API endpoint, I set signal
property of request to AbortSignal.timeout(5_000)
to implement timeout.
According to MDN documentation, AbortSignal.timeout(TIME_IN_MS)
will return AbortSignal
that will automatically abort after specified time. The signal aborts with either TimeoutError
on timeout, or with AbortError
due to pressing a browser stop button, closing the tab (or some other inbuilt "stop" operation).
The response that I get every time when running getData()
is AbortError
with message The user aborted a request.
and never TimeoutError
, even when I set browser to throttle to slow 3G and set time to 100ms. I also tried setting the response to take 10s in the server and in the front end to no throttle and timeout to 9000ms.
Why don't I get TimeoutError
but only get AbortError
?
Update 1:
This issue seems to only be for chromium-based browsers like Google Chrome, Microsoft Edge but works as Expected in Firefox.
Update 2:
As pointed by @Kaiido, this seems to be error of not only Chromium but also Safari. Issue is opened on both:
- Chromium: https://bugs.chromium/p/chromium/issues/detail?id=1431720
- Safari: https://bugs.webkit/show_bug.cgi?id=246069
-
Is
5_000
a typo? – James Commented Apr 9, 2023 at 15:17 - 2 @James no it's not a typo, it's numeric separator which was introduced to the language in ES2021 to improve readability of numeric literal, check out MDN docs. – Aayush Karna Commented Apr 9, 2023 at 15:25
1 Answer
Reset to default 8This is indeed a Chrome and Safari bug.
I just opened CRBUG 1431754, and Safari had BUG 246069 for quite some time now.
According to the specs
To abort a
fetch()
call with a promise, request, responseObject, and an error:
- Reject promise with error.
In this case you hit the abort steps set at the step 11 of the fetch()
method algorithm:
4.11 Abort the
fetch()
call with p, request, responseObject, and requestObject’s signal’s abort reason.
Here "requestObject’s signal’s abort reason" is the object stored in our AbortSignal.reason
, so not only should we have the same kind of DOMException
, but it should even be the same object.
Here is another test (hitting the step 4. this time, but the result is the same) that does repro the issue:
(async () => {
const signal = AbortSignal.timeout(0);
// Wait for the signal times out
await new Promise((res) => setTimeout(res));
const { reason } = signal;
try {
await fetch("./",{ signal });
}
catch(fetchErr) {
console.log({
reason: reason.name, // Expected: "TimeoutError"
fetchErr: fetchErr.name, // Expected: "TimeoutError"
same: fetchErr === reason // Expected: true
});
}
})();
Unfortunately I don't see what you can do about it... checking the original AbortSignal#reason
at the time the fetch()
promise rejects might do it, but that sounds a bit hackish and prone to false positives.