Explain event loop with execution order
Execution order: (1) all synchronous code on the call stack, (2) drain the entire microtask queue, (3) optional render, (4) one macrotask, then back to step 2. So output order is: sync → microtasks → (render) → one macrotask → microtasks again, etc. Work through console.log/setTimeout/Promise puzzles with this rule.
Interviewers love "what's the output order?" puzzles. There's one rule set that solves all of them.
The execution-order rule
1. Run ALL synchronous code (call stack) to completion.
2. Drain the ENTIRE microtask queue.
3. (Browser may render here.)
4. Run ONE macrotask.
5. Go to step 2.So the order is: sync → all microtasks → one macrotask → all microtasks → one macrotask → ...
Puzzle 1
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// 1, 4, 3, 2Sync: 1, 4. Microtasks: 3. Macrotask: 2.
Puzzle 2 — interleaving
console.log("A");
setTimeout(() => {
console.log("B");
Promise.resolve().then(() => console.log("C"));
}, 0);
setTimeout(() => console.log("D"), 0);
Promise.resolve().then(() => console.log("E"));
console.log("F");
// A, F, E, B, C, D- Sync:
A,F. - Microtasks:
E. - Macrotask 1 (first timeout):
B, queues microtaskC. → drain microtasks →C. - Macrotask 2 (second timeout):
D.
Puzzle 3 — async/await
async function f() {
console.log("1");
await null; // everything after await = a microtask
console.log("2");
}
console.log("0");
f();
console.log("3");
// 0, 1, 3, 2f() runs synchronously up to await; console.log("2") is scheduled as a microtask, so 3 (sync) prints first.
The mental checklist
- Mark every line: sync, microtask (
.then,awaitcontinuation,queueMicrotask), or macrotask (setTimeout, events). - Run all sync top-to-bottom.
- Drain microtasks (watch for new ones added during the drain).
- One macrotask → its sync runs, may queue micro/macro tasks.
- Repeat from step 3.
Senior framing
Getting these right consistently — especially await (the continuation is a microtask) and microtasks queued inside a macrotask draining before the next macrotask — signals you actually understand the loop, not just memorized "Promises before setTimeout."
Follow-up questions
- •Why does code after `await` behave like a `.then` callback?
- •In Puzzle 2, why does C print before D?
- •Where would a `requestAnimationFrame` callback fit?
Common mistakes
- •Forgetting that microtasks queued inside a macrotask drain before the next macrotask.
- •Treating `await` continuations as synchronous.
- •Assuming two setTimeout(0)s and a Promise interleave unpredictably.
Edge cases
- •`await` on a non-thenable still defers the continuation to a microtask.
- •Older engines added an extra microtask tick for `await`; modern ones optimized it away.
Real-world examples
- •Standard interview output-prediction questions.