HardAsync & Effects🐛 Debug Challenge

Race condition — earlier slow request overwrites later fast one

Buggy Code — Can you spot the issue?

const log = [];
let currentResult = null;

function fakeSearch(query, delay) {
  return new Promise(resolve =>
    setTimeout(() => resolve(results for: ${query}), delay)
  );
}

// Bug: no cancellation — fast response after slow can overwrite
async function search(query) {
  const result = await fakeSearch(query, query.length < 6 ? 50 : 10);
  currentResult = result; // always sets, even if stale
  log.push('set: ' + result);
}

search('react');        // slow (50ms) — starts first
search('react hooks');  // fast (10ms) — starts second

setTimeout(() => {
  console.log(currentResult); // race condition: 'react' might win!
}, 100);

Fixed Code

const log = [];
let currentResult = null;

function fakeSearch(query, delay) {
  return new Promise(resolve =>
    setTimeout(() => resolve(results for: ${query}), delay)
  );
}

// Fix: request ID — only accept the latest request's result
let latestRequestId = 0;

async function search(query) {
  const requestId = ++latestRequestId;
  const result = await fakeSearch(query, query.length < 6 ? 50 : 10);

  if (requestId === latestRequestId) { // still the latest?
    currentResult = result;
    log.push('set: ' + result);
  } else {
    log.push('discarded: ' + result);
  }
}

search('react');
search('react hooks');

setTimeout(() => console.log(currentResult), 100);

Bug Explained

Bug: 'react' (50ms) starts before 'react hooks' (10ms). 'react hooks' resolves first and sets the result. Then 'react' resolves and overwrites with stale data.

Explanation: 'react' gets requestId=1, 'react hooks' gets requestId=2 (latestRequestId). 'react hooks' resolves first — requestId(2) === latestRequestId(2), sets result. 'react' resolves — requestId(1) !== latestRequestId(2), discarded.

Key Insight: Race condition fix: increment a counter on each request, check if yours is still the latest when it resolves. AbortController is cleaner (actually cancels the request) but request ID is simpler to understand.

More Async & Effects Debug Challenges

MediumState update after component unmountMediumMissing await causes render before data is ready

Practice spotting bugs live →

38 debug challenges with AI hints

🐛 Try Debug Lab