Race conditions in fetches

A user types fast in a search box. Your code fires a fetch on every keystroke. The response for hat arrives after the response for hats, and suddenly the results show hat instead of hats. Congratulations, you just shipped a race condition.

script.js
javascript
let inflight = null;

async function search(query) {
  if (inflight) inflight.abort();
  const controller = new AbortController();
  inflight = controller;

  try {
    const res = await fetch('/api/search?q=' + encodeURIComponent(query), {
      signal: controller.signal,
    });
    const results = await res.json();
    render(results);
  } catch (err) {
    if (err.name === 'AbortError') return; // expected, ignore
    showError(err.message);
  }
}

AbortController cancels stale fetches so only the latest one wins.

Read the code top to bottom. Every search call aborts the previous one and starts a new AbortController. The stale fetch throws an AbortError, which we explicitly ignore. Only the most recent fetch ever reaches render. This pattern is how every production search box handles the same problem.

Validation checklist: Prove the race condition

Loading practice…

Quiz: Quiz

Loading practice…