GitHub

Copyright © 2026 Sumit Paul

Design by Sumit Paul•LinkedIn

LinkedIn

Async race condition in search results

Avatar

Sumeet Kumar Paul

Frontend Engineer

2 min readSource code
Feb 10, 2026

Problem

Under rapid input changes and slow network conditions, multiple search requests can remain in flight at the same time. When responses resolve out of order, older results may overwrite newer state, causing the UI to display incorrect data.

The implementation assumes that the most recent request will resolve last. This assumption breaks under real-world network latency and asynchronous execution.

  • ●Multiple async requests in flight simultaneously.
  • ●Variable or throttled network latency.
  • ●Rapid user input before previous requests resolve.
  • ●State updates tied directly to promise resolution.
page.tsx
srcapppracticeasync-race-condition-in-search-resultspage.tsx
1useEffect(() => {
2  if (!query.trim()) return;
3
4  void (async () => {
5    try {
6      const data = await fetchResults(query);
7      console.log("commit results for:", query);
8      setResults(data);
9    } catch (error) {
10      console.error("search request failed", error);
11    }
12  })();
13}, [query]);

This implementation works in happy paths but does not guard against out-of-order async resolution. Any response that resolves later is allowed to update state, regardless of when it was initiated.

Solution

Each request is assigned an identity, and state updates are only allowed from the most recent request. Responses that resolve out of order are ignored instead of committing stale data.

page.tsx
srcapppracticeasync-race-condition-in-search-resultspage.tsx
1const requestIdRef = useRef(0);
2
3useEffect(() => {
4  if (!query.trim()) {
5    requestIdRef.current = 0;
6    return;
7  }
8
9  const requestId = ++requestIdRef.current;
10
11  void (async () => {
12    try {
13      const data = await fetchResults(query);
14      if (requestId === requestIdRef.current) {
15        console.log("commit results for:", query);
16        setResults(data);
17      } else {
18        console.log("ignored stale response for:", query);
19      }
20    } catch (error) {
21      console.error("search request failed", error);
22    }
23  })();
24}, [query]);

By guarding state updates with request identity, the UI becomes resilient to async timing differences and network variability.

This issue is not specific to search, debouncing, or React. It occurs whenever UI state is updated directly from asynchronous work without guarding against out-of-order resolution. Treating async responses as untrusted by default and committing state only when intent is still valid prevents an entire class of UI correctness bugs.

Keywords

async race condition, react state, frontend edge case, ui correctness, search