fetch with Timeout

By  on  

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.

Recent Features

  • By
    9 Mind-Blowing WebGL Demos

    As much as developers now loathe Flash, we're still playing a bit of catch up to natively duplicate the animation capabilities that Adobe's old technology provided us.  Of course we have canvas, an awesome technology, one which I highlighted 9 mind-blowing demos.  Another technology available...

  • By
    fetch API

    One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to...

Incredible Demos

  • By
    jQuery Chosen Plugin

    Without a doubt, my least favorite form element is the SELECT element.  The element is almost unstylable, looks different across platforms, has had inconsistent value access, and disaster that is the result of multiple=true is, well, a disaster.  Needless to say, whenever a developer goes...

  • By
    Link Nudging with CSS3 Animations

    One of the more popular and simple effects I've featured on this blog over the past year has been linking nudging.  I've created this effect with three flavors of JavaScript:  MooTools, jQuery, and even the Dojo Toolkit.  Luckily CSS3 (almost) allows us to ditch...

Discussion

  1. Bas

    Can’t you do the clearTimeout(timer); in a finally block? Instead of both in the regular flow and the catch.

  2. Chris

    There’s also AbortSignal.timeout(). I think it’s about as well supported as signal by now, but pretty easy to shim.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!