Interview answer drill

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

Microtasks vs Macrotasks in JavaScript (Event Loop Priority)Frontend interview answer

HighHardJavascript
Interview focus

This JavaScript interview question tests whether you can explain Microtasks vs macrotasks: event-loop priority, paint starvation, and yielding, connect it to production trade-offs, and handle common follow-up questions.

  • Microtasks vs macrotasks: event-loop priority, paint starvation, and yielding explanation without falling back to memorized docs wording
  • Event Loop and Async 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

In each event loop turn, JavaScript runs synchronous code, then drains the entire microtask queue before the next macrotask. That priority explains why Promises run before timers, why spinners can fail to paint, and when to yield with macrotasks or animation frames.

Full interview answer

Core difference

Both are “queued callbacks” that run after the current synchronous call stack finishes. The difference is priority and timing.

  • Microtasks run immediately after the call stack and are fully drained before anything else happens.
  • Macrotasks run one at a time on the next event loop turn, and the browser may render between macrotasks.

That is why a loading spinner can stay invisible if a recursive Promise chain keeps the current turn alive.

Queue

Common sources

When it runs

Why it matters

Microtask queue

Promise.then/catch/finally, queueMicrotask, MutationObserver

After the current call stack, before the next macrotask and typically before rendering

Higher priority: can “cut in line” before timers/events

Macrotask queue

setTimeout, setInterval, DOM events, message events, I/O callbacks

Next event loop turn; one macrotask runs, then microtasks drain again

Lets the browser breathe: rendering/input can happen between turns

Microtasks are higher priority than macrotasks.

Event loop rule of thumb (browser)

For each turn:
1) Run all synchronous code (call stack)
2) Drain microtasks completely
3) Run one macrotask
4) Render (if needed) / handle UI
5) Repeat

JAVASCRIPT
console.log('A');

setTimeout(() => console.log('B (macrotask: timer)'), 0);

Promise.resolve()
  .then(() => console.log('C (microtask: promise)'))
  .then(() => console.log('D (microtask: promise)'));

console.log('E');

// Output:
// A
// E
// C
// D
// B
                  

Interview punchline: “Promises run before timers because microtasks are drained before the next macrotask.”

Common pitfall: microtasks can starve rendering

Because the microtask queue is drained completely, a long/recursive microtask chain can delay painting and make the UI feel frozen.

JAVASCRIPT
function starveUI() {
  queueMicrotask(starveUI); // or Promise.resolve().then(starveUI)
}
starveUI();

// Result: browser can't yield to rendering/input.
                  

Practical guidance

  • Use microtasks for “run right after this finishes” follow-ups (e.g., finalize state, chain promise work).
  • Use macrotasks to yield control (e.g., split heavy work so the UI can render):
  • setTimeout(fn, 0) (coarse)
  • requestAnimationFrame (before next paint)
  • requestIdleCallback (when idle; best-effort)

Browsers may render between macrotasks, but not while the microtask queue is still draining.

JAVASCRIPT
// Yield so the UI can render between chunks
function doChunkedWork(items, chunkSize = 200) {
  let i = 0;
  function runChunk() {
    const end = Math.min(i + chunkSize, items.length);
    for (; i < end; i++) {
      // heavy work
    }
    if (i < items.length) setTimeout(runChunk, 0); // macrotask yield
  }
  runChunk();
}
                  

Practical incident
A search page sets loading=true, then kicks off a recursive Promise chain to normalize 20k rows before painting the next frame. Because the microtask queue never empties, the spinner may not paint until the whole chain finishes.

Fix
Keep tiny follow-ups in microtasks, but move chunked CPU work behind requestAnimationFrame or a macrotask so the browser can paint and accept input.

Node.js note (don’t overclaim)

Node also has microtasks (Promises) and macrotask-like phases (timers, I/O, check, etc.). Microtasks are processed between phases, and process.nextTick has even higher priority than Promise microtasks. In interviews: keep the core browser model correct, then mention Node differences briefly if asked.

One-sentence answer

Microtasks (Promises/queueMicrotask) run immediately after the current stack and are drained fully before moving on, while macrotasks (timers/events) run one per turn, allowing the runtime/browser to render between turns.

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.