Interview answer drill

Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Async Race Conditions and Stale UI UpdatesFrontend interview answer

HighIntermediateJavascript
Interview focus

This JavaScript interview question tests whether you can explain Debug async race conditions: stale UI, cancellation, and latest-result guards, connect it to production trade-offs, and handle common follow-up questions.

  • Debug async race conditions: stale UI, cancellation, and latest-result guards explanation without falling back to memorized docs wording
  • Async and Concurrency reasoning, edge cases, and production failure modes
  • How you would answer the most likely JavaScript interview follow-up
Practice more JavaScript interview questions
Interview quick answer

Debug the production bug where async requests resolve out of order and overwrite newer UI state. The fix is cancellation, request identity, shared-controller ownership, or takeLatest-style guards that stop stale results from winning.

Full interview answer

The core issue

This is a classic production debug problem in search, filters, autosave flows, and route-driven detail pages. Request A starts, then request B starts, B finishes first, and the UI briefly looks correct. When A finishes later, it overwrites newer state with stale data unless your code cancels or guards the older request.

Step

What happens

User types 'rea'

Request A is sent

User types 'react'

Request B is sent

Request B returns first

UI shows results for 'react'

Request A returns later

UI is overwritten with stale 'rea' results

Classic search-as-you-type race condition; autosave and navigation races behave the same way.

How to prevent it

You need either real cancellation or a stale-result guard. Both prevent old responses from winning the race.

Technique

How it works

Notes

AbortController

Cancel the previous request so it never resolves

Best when the API supports AbortSignal (e.g., fetch)

Request id guard

Only apply results if the id matches the latest

Works even if the API cannot be cancelled

takeLatest / switchMap

Wrap calls to auto-cancel or ignore stale results

Common in RxJS or custom utilities

Three reliable ways to avoid stale updates.
JAVASCRIPT
let requestId = 0;

async function search(query) {
  const id = ++requestId;
  const res = await fetch(`/api?q=${query}`);
  const data = await res.json();
  if (id !== requestId) return; // stale result
  render(data);
}
                  
JAVASCRIPT
let controller;

async function search(query) {
  if (controller) controller.abort();
  controller = new AbortController();
  const res = await fetch(`/api?q=${query}`, { signal: controller.signal });
  const data = await res.json();
  render(data);
}
                  

When the async work cannot be aborted

AbortController only helps when the underlying task listens to AbortSignal. Autosave pipelines, IndexedDB work, and some library promises still need a latest-version guard so stale completions cannot win.

JAVASCRIPT
let latestDraftVersion = 0;

async function autosave(draft) {
  const version = ++latestDraftVersion;
  const saved = await saveDraftToLocalAndRemote(draft); // not abortable
  if (version !== latestDraftVersion) return;
  showSavedAt(saved.updatedAt);
}
                  

Shared-controller follow-up

If one route transition starts several fetches, a single AbortController can own the whole screen load. Aborting on route leave cancels profile, permissions, and related requests together instead of leaking old work into the next screen.

Pitfalls

  • Promise.race does not cancel the losing promises.
  • Debounce reduces request count but does not prevent out-of-order responses.
  • Always clean up abort listeners or timers to avoid leaks.
Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the JavaScript interview questions cluster or a guided prep path.