Web APIsJavaScriptMedium

How does AbortController work?

01

The Short Answer

AbortController is a Web API that lets you cancel asynchronous operations — most commonly fetch requests, but also event listeners, streams, and any custom async work. You create a controller, pass its signal to the operation you want to be able to cancel, and call controller.abort() when you want to stop it. The operation receives an abort event and terminates gracefully.

02

Basic Usage with Fetch

The most common use case is cancelling HTTP requests. You create an AbortController, pass its signal to fetch, and call abort() when the request is no longer needed. The fetch promise rejects with an AbortError that you can catch and handle differently from real network errors.

abort-fetch.tstypescript
const controller = new AbortController();

// Pass the signal to fetch
const response = fetch('/api/search?q=javascript', {
  signal: controller.signal,
});

// Cancel the request after 5 seconds (timeout)
setTimeout(() => controller.abort(), 5000);

// Or cancel immediately based on user action
cancelButton.addEventListener('click', () => controller.abort());

// Handle the response (or cancellation)
try {
  const res = await response;
  const data = await res.json();
  renderResults(data);
} catch (error) {
  if (error instanceof DOMException && error.name === 'AbortError') {
    console.log('Request was cancelled');
    // Don't show error UI — this was intentional
  } else {
    console.error('Real network error:', error);
    showErrorMessage();
  }
}

The key pattern: always check if the error is an AbortError before showing error UI. Aborts are intentional cancellations, not failures — you don't want to flash an error message when the user simply navigated away or typed a new search query.

03

In React: Cancelling on Unmount

The most important React use case is cancelling fetch requests when a component unmounts. Without this, a request that completes after unmount tries to call setState on a component that no longer exists — causing memory leaks and the classic "Can't perform a React state update on an unmounted component" warning.

react-abort.tsxtypescript
function SearchResults({ query }: { query: string }) {
  const [results, setResults] = useState<Result[]>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);

    async function fetchResults() {
      try {
        const res = await fetch(`/api/search?q=${query}`, {
          signal: controller.signal,
        });
        const data = await res.json();
        setResults(data.results);
      } catch (error) {
        if (error instanceof DOMException && error.name === 'AbortError') {
          return; // Component unmounted or query changed — do nothing
        }
        console.error('Search failed:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchResults();

    // Cleanup: abort the request if query changes or component unmounts
    return () => controller.abort();
  }, [query]);

  return loading ? <Spinner /> : <ResultList items={results} />;
}

When query changes, React runs the cleanup function from the previous effect (which aborts the old request) before running the new effect. This means only the latest request's results will be applied to state — previous in-flight requests are cancelled automatically.

04

Race Condition Prevention

AbortController solves the classic race condition where a user types quickly in a search box, triggering multiple requests. Without cancellation, responses can arrive out of order — an older, slower request might resolve after a newer one, overwriting the correct results with stale data.

🏎️

The pizza delivery race condition

You order pizza, then change your mind and order sushi. Without cancellation, both deliveries are in progress. If the sushi arrives first and you eat it, then the pizza arrives — you've got stale food you didn't want. AbortController is like calling the pizza place to cancel before it's delivered.

05

AbortSignal.timeout() and AbortSignal.any()

Modern browsers provide static helper methods on AbortSignal for common patterns. AbortSignal.timeout() creates a signal that auto-aborts after a duration — no manual controller needed. AbortSignal.any() combines multiple signals so the operation aborts if any of them fires.

signal-helpers.tstypescript
// Auto-timeout after 5 seconds (no controller needed)
const response = await fetch('/api/data', {
  signal: AbortSignal.timeout(5000),
});

// Combine signals: abort on timeout OR user cancellation
const controller = new AbortController();
cancelButton.onclick = () => controller.abort();

const response = await fetch('/api/data', {
  signal: AbortSignal.any([
    AbortSignal.timeout(10000),   // 10s timeout
    controller.signal,             // Manual cancel
  ]),
});

AbortSignal.timeout() is cleaner than creating a controller and calling setTimeout yourself. AbortSignal.any() is useful when you have multiple reasons to cancel — timeout, user action, component unmount — and want any of them to trigger the abort.

06

Beyond Fetch: Other Uses

AbortController isn't limited to fetch. Any API that accepts an AbortSignal can be cancelled, and you can also use it in your own async functions to support cooperative cancellation.

other-uses.tstypescript
// Cancel an event listener
const controller = new AbortController();
window.addEventListener('resize', handleResize, { signal: controller.signal });
// Later: controller.abort() removes the listener automatically

// Custom async function that respects abort
async function processLargeDataset(
  data: Item[],
  signal: AbortSignal
): Promise<Result[]> {
  const results: Result[] = [];

  for (const item of data) {
    // Check if cancelled before each expensive operation
    if (signal.aborted) {
      throw new DOMException('Processing cancelled', 'AbortError');
    }
    results.push(await processItem(item));
  }

  return results;
}
07

Why Interviewers Ask This

This question tests whether you understand async operation lifecycle management — a critical skill for building responsive UIs. Interviewers want to see that you know how to prevent memory leaks in React (abort on unmount), handle race conditions (abort stale requests), implement timeouts, and distinguish intentional cancellations from real errors. It shows you think about the unhappy paths, not just the success case.

Quick Revision Cheat Sheet

Create: const controller = new AbortController()

Pass signal: fetch(url, { signal: controller.signal })

Cancel: controller.abort() — rejects with AbortError

React cleanup: Return () => controller.abort() from useEffect

Timeout shortcut: AbortSignal.timeout(5000) — no controller needed

Error check: error.name === 'AbortError' → intentional, don't show error UI