Event Loop & Microtasks

Hard100% Free~25 mins#event-loop#microtasks#macrotasks#promises#settimeout#queuemicrotask
Key Learning Objectives
✓

Trace asynchronous JavaScript execution across the Call Stack, Microtask Queue, and Macrotask (Task) Queue.

✓

Understand the HTML5 Event Loop specification ordering: Synchronous -> Drain Microtasks -> Render/Paint -> Next Macrotask.

✓

Predict console output ordering with mixed Promises, setTimeout, queueMicrotask, and async/await.

✓

Explain how recursive microtasks can starve the macrotask queue and freeze the browser UI.

The Interview Problem

What is the exact order of numbers printed to the console when the following JavaScript snippet executes?

1console.log(1);
2
3setTimeout(() => {
4 console.log(2);
5}, 0);
6
7Promise.resolve()
8 .then(() => {
9 console.log(3);
10 return Promise.resolve(4);
11 })
12 .then((val) => {
13 console.log(val);
14 });
15
16queueMicrotask(() => {
17 console.log(5);
18});
19
20console.log(6);
Predict Console Output
Interactive Challenge

Select the option that matches what standard ECMAScript prints to the console:

1 6 3 5 4 2

1 2 3 4 5 6

1 6 2 3 5 4

1 6 3 4 5 2

V8 Engine Execution Trace
Step 1 of 9 (Line 1)

Synchronous execution: log 1 directly to console.

Call Stack (Top = Active)
Global: console.log(1)
Lexical Scope / BindingsNo active local bindings
Console Stream
> 1

Deep Technical Breakdown

The HTML Event Loop Processing Model

The Event Loop continuously coordinates execution between three primary components:

  1. Call Stack (Synchronous Code): All synchronous code executes to completion first. Lines 1 and 20 run immediately, producing 1 and 6.
  2. Microtask Queue: Populated by Promise.then/catch/finally, queueMicrotask, and MutationObserver.
    • Critical Rule: As soon as the Call Stack is empty, the engine enters a Microtask Checkpoint and drains the entire Microtask Queue, including any new microtasks enqueued while processing existing microtasks.
    • Microtask 1 runs: logs 3 and returns Promise.resolve(4), scheduling its .then handler.
    • Microtask 2 runs: logs 5.
    • Microtask 3 runs: receives resolved value 4 and logs 4.
  3. Macrotask Queue (Task Queue): Populated by setTimeout, setInterval, setImmediate (Node), and I/O.
    • A macrotask is only picked AFTER the microtask queue has reached zero tasks. Thus, the setTimeout callback logs 2 last.
Common Traps & Mistakes

Believing `setTimeout(fn, 0)` means zero delay execution. In browsers, timers have a minimum clamp (typically 4ms after nested depth) and always wait behind microtasks.

Assuming `async/await` is fundamentally different from Promises. An `async` function is syntactic sugar over generator-like Promise chaining; code up to the first `await` is synchronous, and the remainder is enqueued as a microtask.

Recursively calling `queueMicrotask` or `Promise.resolve().then(recurse)`, which indefinitely starves the macrotask queue and hangs UI rendering.

FAANG Follow-Up Probes
Probe #1

How does the browser rendering/paint step fit into the event loop cycle relative to microtasks and macrotasks?

Probe #2

How does Node.js's event loop (libuv phases: timers, pending callbacks, poll, check, close) differ from the browser HTML event loop?

Probe #3

Why does `requestAnimationFrame` execute before layout and paint, and how does it relate to macrotask scheduling?