Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to set a timeout for fetch

Short answer

fetch() itself waits as long as needed. AbortSignal.timeout() creates a signal that cancels the request after the given number of milliseconds:

const response = await fetch('/api/report', { signal: AbortSignal.timeout(5000) });

Recognizing a timeout

On a timeout the error has the name TimeoutError, on a manual cancel it is AbortError:

try {
  const response = await fetch('/api/report', { signal: AbortSignal.timeout(5000) });
  const report = await response.json();
} catch (error) {
  if (error.name === 'TimeoutError') {
    showMessage('The server is taking too long. Try again later.');
  } else {
    throw error;
  }
}

With AbortController

AbortController gives a signal you can cancel yourself, for example with a button or on a timer:

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5000);
 
try {
  const response = await fetch('/api/report', { signal: controller.signal });
  console.log(await response.json());
} finally {
  clearTimeout(timer);
}

How it works

Any promise-based operation that accepts a signal behaves the same way. A small example without the network:

const signal = AbortSignal.timeout(50);
 
await new Promise((resolve) => setTimeout(resolve, 100));
 
console.log(signal.aborted, signal.reason.name);

Output:

true TimeoutError

Common mistake

A timer with setTimeout() alone does not stop the request: it keeps loading in the background. Pass a signal to fetch() so the request is really cancelled.

// the request continues even after the message
setTimeout(() => showMessage('Too slow'), 5000);
const response = await fetch('/api/report');

Functions used in this recipe

  • fetch — Sends an HTTP request and returns a promise of the response.

Thanks for reading!

Donate to the site — we work hard for you.