🟑 MediumEvent Loop TrapsπŸ› Debug Challenge

Mixing microtasks and DOM updates

Buggy Code β€” Can you spot the issue?

async function processData(data) {
  statusEl.textContent = 'Loading...'; // DOM write

  // Immediately await a microtask β€” still blocks render!
  await Promise.resolve();

  const result = heavyComputation(data); // blocks for 2 seconds
  statusEl.textContent = 'Done: ' + result;
}

Fixed Code

async function processData(data) {
  statusEl.textContent = 'Loading...';

  // setTimeout(0) = macrotask β€” yields to the browser's render phase
  await new Promise(resolve => setTimeout(resolve, 0));

  // Now the browser has had a chance to paint "Loading..."
  const result = heavyComputation(data);
  statusEl.textContent = 'Done: ' + result;
}

// Even better: use requestAnimationFrame to align with render
async function processDataRaf(data) {
  statusEl.textContent = 'Loading...';
  await new Promise(resolve => requestAnimationFrame(resolve));
  const result = heavyComputation(data);
  statusEl.textContent = 'Done: ' + result;
}

Bug Explained

Bug: Promise.resolve() is a microtask β€” it runs before the browser has a chance to render. The repaint is still blocked by the heavy computation that runs right after.

Explanation: Microtasks (Promise.resolve) run before the browser paint. To yield for rendering, use setTimeout(0) which is a macrotask β€” the browser gets a render frame between macrotasks.

Key Insight: To yield to the browser render phase, use setTimeout(0) or requestAnimationFrame β€” NOT Promise.resolve(). Microtasks drain before any painting occurs.

More Event Loop Traps Debug Challenges

🟑 MediumUI update blocked by sync codeβ†’πŸ”΄ HardMicrotask starvation β€” UI never updatesβ†’πŸŸ‘ MediumsetTimeout inside a loop β€” all fire at onceβ†’πŸ”΄ HardGenerator function called synchronously inside Promiseβ†’

Practice spotting bugs live β†’

38 debug challenges with AI hints

πŸ› Try Debug Lab