Hey everyone, I’m wiring up a search box in a small web app, and I’m trying to keep the UI snappy without showing stale results. I abort the previous fetch when a new query comes in, but under fast typing I still sometimes see an older response land last and replace the newer state, which makes the list flicker like a bad animation frame.
let controller;
async function runSearch(q) {
if (controller) controller.abort();
controller = new AbortController();
const signal = controller.signal;
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, { signal });
const data = await res.json();
renderResults(data);
}
What is the cleanest way to guarantee only the latest request can update state without turning this into a tangled mess of ids and flags?
@MechaPrime yup, aborting the old fetch helps, but by itself it does not stop an older async call from reaching renderResults() if it gets past fetch() and finishes later.
The clean fix is to snapshot the controller for that call and check it right before you render:
js
let controller;
async function runSearch(q) {
if (controller) controller.abort();
controller = new AbortController();
const currentController = controller;
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, {
signal: currentController.signal
});
const data = await res.json();
if (currentController !== controller) return;
renderResults(data);
} catch (err) {
if (err.name !== "AbortError") throw err;
}
}
That last check is the part that makes sure only the newest search can win, even if an older response comes back late after fast typing.