What happened
Our search box was already debounced, but fast typists still saw the results flicker. Sometimes the list showed results for an earlier query a moment after the right ones had appeared.
The debounce was working as intended. The issue was somewhere else: two requests were in flight at once and they did not finish in the order we sent them.
Why it happens
Debounce only reduces how many requests you send. It says nothing about the order they come back in. If a user pauses after typing "re", then continues to "react", both requests can be in flight together, and the network or server may answer the older one last.
Whichever response arrives last wins when the code sets state. If that is the stale one, it overwrites the newer results. This is a classic race condition, and it also means the server may keep working on a query that nobody is waiting for any more.
What we do now
We create an AbortController for each request and abort the previous one before starting the next. The aborted fetch rejects with an AbortError, which we ignore, so only the latest request can ever update the UI.
Cancellation also helps the backend when it notices a closed connection, although how much it saves depends on the server stopping work when the client disconnects. Even when it does not, the UI is protected from stale data.
let controller: AbortController | undefined
async function search(query: string) {
controller?.abort()
controller = new AbortController()
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: controller.signal,
})
return await res.json()
} catch (err) {
if ((err as Error).name === 'AbortError') return undefined
throw err
}
}Quick checklist
- 1Keep a ref to the current AbortController.
- 2Abort it before starting a new request.
- 3Pass its signal to fetch.
- 4Ignore errors where error.name is AbortError.
- 5Abort on unmount too, so late responses do not set state.
Rule of thumb
Debounce limits how many requests you send, and cancellation makes sure only the latest one can win.