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's really being asked
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.
The full answer
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.
The mistakes people make
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.
What usually comes next
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.
A 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.
Interview question
In a search-as-you-type component using AbortController, what must occur before initiating a new fetch on each keystroke?
- a.Rely on debouncing alone because it guarantees responses arrive in input order
- b.Reuse the same AbortSignal across all keystrokes to minimize object creation
- c.Abort the previous controller, instantiate a new one, and pass its fresh signalCorrect
- d.Increment a request ID and drop stale responses while leaving fetches in flight
Why? this is the answer
You must abort the previous in-flight request and create a fresh AbortController per keystroke because a signal can only be aborted once. Reusing the same AbortSignal is a tempting mistake because once aborted it would immediately reject all subsequent fetches.
Just read this? Test yourself on what you have been reading.
Read the original → developer.mozilla.org
- #typescript
- #web-apis
- #abortcontroller
- #fetch
- #race-conditions
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on typescript — each one lists the topics its interview covers.
See open roles