How would you prevent search-as-you-type race conditions with AbortController?

Canceling stale fetches to keep the UI consistent.
Abort the previous request before each new keystroke, pass the fresh signal into fetch, and ignore the AbortError.
WHAT THIS TESTS: This question probes your grasp of asynchronous control flow in the browser. Interviewers want to see that you treat every in-flight request as a side effect that must be cleaned up when newer input arrives. The core issue is not network speed but UI consistency: if request A starts before request B but finishes after it, the component must never render A's payload. The AbortController API is the platform-native primitive for this, and a senior candidate should explain its lifecycle integration without being prompted.
A GOOD ANSWER COVERS: Four things in order. First, scope: you keep a single AbortController instance in a closure, class field, or ref so it survives across renders. Second, replacement: on every keystroke you call abort on the existing controller, instantiate a new one, and pass its signal into the fetch call via the signal option. Third, error handling: because abort triggers a rejection with a DOMException named AbortError, you must catch it and swallow it silently rather than surfacing it as a user-facing error. Fourth, cleanup: on component unmount you abort the last pending request to prevent memory leaks and state updates on unmounted components.
COMMON WRONG ANSWERS: Several patterns are red flags. One is claiming that debouncing alone removes the race; debounce only spaces out requests, it does not guarantee ordering because network latency is variable. Another is using a monotonically increasing request ID and dropping responses that do not match the latest ID; this works but ignores the platform primitive the interviewer asked about and leaves unnecessary network traffic in flight. A third is forgetting to catch the AbortError, which causes unhandled promise rejections and can break error boundaries. Finally, reusing the same AbortSignal for multiple fetches is wrong because a signal can only be aborted once; after that every subsequent fetch using it rejects immediately.
LIKELY FOLLOW-UPS: An interviewer might ask how you would handle multiple concurrent fetches that are not replacements, such as a search plus a preview fetch, which is where AbortSignal.any comes in. They might also ask how to add a hard timeout, which you can do with AbortSignal.timeout and combine with your explicit controller. Another variant is how this interacts with React Strict Mode double-mounting, which tests whether you understand cleanup semantics in modern frameworks.
ONE CONCRETE EXAMPLE: Imagine a React component with a query state. You hold const abortControllerRef = useRef(null). Inside the effect that runs when query changes, you first check if abortControllerRef.current exists and call abort on it. You then assign abortControllerRef.current = new AbortController. You call fetch(url, { signal: abortControllerRef.current.signal }) and in the catch block you check if error.name === 'AbortError' and return early. In the cleanup function returned by useEffect you call abortControllerRef.current?.abort. This guarantees that only the newest request can update the suggestion list.
Source: developer.mozilla.org
Read the original → developer.mozilla.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.