Post cover

How to Timeout a fetch() Request

Updated March 20, 2023

You should not rely on the network when working with it. The network is unreliable because an HTTP request or response can fail:

  • The user is offline
  • DNS lookup failed
  • The server doesn't respond
  • The server responds but with an error
  • and more.

Users are OK to wait up to 8 seconds for simple requests to complete. That's why you need to set a timeout on the network requests and inform the user after 8 seconds about the network problems.

I'll show you how to use setTimeout(), the abort controller, and fetch() to implement configurable request timeouts.

1. Default fetch() timeout

By default a fetch() request timeouts at the time indicated by the browser. In Chrome a network request timeouts at 300 seconds, while in Firefox at 90 seconds.


async function loadGames() {
const response = await fetch('/games');
// fetch() timeouts at 300 seconds in Chrome
const games = await response.json();
return games;
}

300 seconds and even 90 seconds are way more than a user would expect a network request to complete.

As an experiment, in the demo the /games API response takes 301 seconds. Click Load games button to start the request, and it will timeout at 300 seconds (in Chrome).

2. Timeout a fetch() request

fetch() API by itself doesn't allow canceling programmatically a request. To stop a request at the desired time you also need an abort controller.

The following fetchWithTimeout() is an improved version of fetch() that creates requests with a configurable timeout:


async function fetchWithTimeout(resource, options = {}) {
const { timeout = 8000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}

First, const { timeout = 8000 } = options extracts the timeout param in milliseconds from the options object (defaults to 8 seconds).

const controller = new AbortController() creates an instance of the abort controller. This controller allows you stop fetch() requests at will. A new abort controller must be created for each request, in other words, controllers aren't reusable.

const id = setTimeout(() => controller.abort(), timeout) starts a timing function. After timeout time, if the timing function wasn't cleared, controller.abort() cancels the fetch request.

Next line await fetch(resource, { ...option, signal: controller.signal }) starts properly the fetch request. Note the special controller.signal value assigned to signal property: it connectes fetch() with the abort controller.

Finally, clearTimeout(id) clears the abort timing function if the request completes earlier than timeout time passed.

Now here's how to use fetchWithTimeout():


async function loadGames() {
try {
const response = await fetchWithTimeout('/games', {
timeout: 6000
});
const games = await response.json();
return games;
} catch (error) {
// Timeouts if the request takes
// longer than 6 seconds
console.log(error.name === 'AbortError');
}
}

Open the demo.

fetchWithTimeout() (instead of simple fetch()) starts a request that will be canceled at timeout time — 6 seconds.

If the request to /games hasn't finished in 6 seconds, then the request is canceled and a timeout error is thrown.

You can use the expression error.name === 'AbortError' inside the catch block to determine if there was a request timeout.

Open the demo and click Load games button. The request to /games timeouts because it takes longer than 6 seconds.

3. AbortSignal.timeout()

Current browsers support a simpler approach using AbortSignal.timeout():


async function loadGames() {
try {
const response = await fetch('/games', {
signal: AbortSignal.timeout(5000)
});
const games = await response.json();
return games;
} catch (error) {
// Timeouts if the request takes
// longer than 6 seconds
console.log(error.name === 'AbortError');
}
}

4. Summary

By default a fetch() request timeouts at the time set up by the browser. In Chrome, for example, this setting is 300 seconds. That's way longer than a user would expect for a network request to complete.

A good approach to network requests is to configure a request timeout of about 8 - 10 seconds.

Using setTimeout() and abort controller you can create fetch() requests that are configured to timeout when you'd like to.

Check the browser support for the abort controller. There's also a polyfill for it.

Note that there's no way to stop a fetch() request without using an abort controller. Don't use solutions like this.

What good practices regarding network requests do you know? Please write a comment!

Like the post? Please share!

Dmitri Pavlutin

About Dmitri Pavlutin

Software developer and sometimes writer. My daily routine consists of (but not limited to) drinking coffee, coding, writing, overcoming boredom 😉. Living in the sunny Barcelona. 🇪🇸