Skip to content

JavaScript Tutorial The Fetch API

Cancelling fetch() with AbortController

Lesson 156 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

Sometimes you want to stop a request: the user left the page, typed a new search, or it is simply taking too long. AbortController does that.

script.js
JS
const controller = new AbortController();

fetch("/api/slow", { signal: controller.signal })
  .then(response => response.json())
  .catch(error => {
    if (error.name === "AbortError") {
      console.log("Request was cancelled");
    } else {
      console.log("Real error", error);
    }
  });

controller.abort();      // cancel it

How it works

  1. Create an AbortController.
  2. Pass its signal in the fetch options.
  3. Call controller.abort(). The request stops, and the promise rejects with an error whose name is "AbortError".

You should treat that error as expected, not as a failure to show to the user.

Try the mechanism

script.js
JS
const controller = new AbortController();

controller.signal.addEventListener("abort", () => console.log("abort event fired"));

console.log(controller.signal.aborted); // → false
controller.abort();
console.log(controller.signal.aborted); // → true

A timeout

The old pattern uses a timer:

script.js
JS
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetch(url, { signal: controller.signal });
  clearTimeout(timer);
  // ...
} catch (error) {
  // AbortError after 5 seconds
}

Modern browsers have a shortcut, AbortSignal.timeout():

script.js
JS
try {
  const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
} catch (error) {
  if (error.name === "TimeoutError") {
    console.log("The server took too long.");
  }
}

(A timeout from AbortSignal.timeout rejects with a TimeoutError, while a manual abort() gives an AbortError.) Check browser support if you must support older browsers, and fall back to the timer version.

Search-as-you-type: ignore stale requests

When a user types quickly, an older, slower request could finish after a newer one and show the wrong results. Cancel the previous request when starting a new one.

script.js
JS
let controller;

async function search(term) {
  controller?.abort();                    // cancel the previous request
  controller = new AbortController();

  try {
    const response = await fetch("/api/search?q=" + encodeURIComponent(term), { signal: controller.signal });
    const results = await response.json();
    render(results);
  } catch (error) {
    if (error.name !== "AbortError") showError(error);
  }
}

Aborting other things

The same signal works for event listeners (addEventListener(type, fn, { signal })) and many other APIs, so one abort() can clean up several things at once.

info
Race does not cancel
Promise.race for timeouts leaves the request running. AbortController actually stops it. See Promise.race.