fetch with Timeout
A few years back I wrote a blog post about how write a fetch
Promise that times out. The function was effective but the code wasn't great, mostly because AbortController
, which allows you to cancel a fetch Promise, did not yet exist. With AbortController
and AbortSignal
available, let's create a better JavaScript function for fetching with a timeout:
AbortSignal
instances now feature a timeout
option to time the Promise
out after a given amount of milliseconds:
async function fetchWithTimeout(url, opts = {}, timeout = 5000) { // Create a signal with timeout const signal = AbortSignal.timeout(timeout); // Make the fetch request const _fetchPromise = fetch(url, { ...opts, signal, }); // Await the fetch with a catch in case it's aborted which signals an error const result = await _fetchPromise; return result; }; // Usage try { const impatientFetch = await fetchWithTimeout('/', {}, 2000); } catch(e) { console.log("fetch possibly canceled!", e); }
While formerly the AbortSignal
would come from an AbortController
, you can now use AbortSignal.timeout
to create the signal.
At the moment, however, only edge browser versions support AbortSignal.timeout
. So much like the original function, an alternative function could use setTimeout
to time to the cancellation but we'll use the signal
with the fetch
request:
async function fetchWithTimeout(url, opts = {}, timeout = 5000) { // Create the AbortController instance, get AbortSignal const abortController = new AbortController(); const { signal } = abortController; // Make the fetch request const _fetchPromise = fetch(url, { ...opts, signal, }); // Start the timer const timer = setTimeout(() => abortController.abort(), timeout); // Await the fetch with a catch in case it's aborted which signals an error try { const result = await _fetchPromise; clearTimeout(timer); return result; } catch (e) { clearTimeout(timer); throw e; } }; // Usage try { const impatientFetch = await fetchWithTimeout('/', {}, 2000); } catch(e) { console.log("fetch possibly canceled!", e); }
The JavaScript code above is much cleaner now that we have a proper API to cancel fetch
Promise calls. Attaching the signal
to the fetch request allows us to use a setTimeout
with abort
to cancel the request after a given amount of time.
It's been excellent seeing AbortController
, AbortSignal
, and fetch
evolve to make async
requests more controllable without drastically changing the API.
Can’t you do the
clearTimeout(timer);
in a finally block? Instead of both in the regular flow and the catch.There’s also
AbortSignal.timeout()
. I think it’s about as well supported as signal by now, but pretty easy to shim.