6.5 — Microtasks vs macrotasks
The 6.2 machine had one queue. Small confession: it has two — and the second one has priority boarding. Promise reactions don't wait in the regular callback queue with the timers and clicks; they ride the microtask queue — the express lane.
The rule that decides every ordering puzzle: when the stack empties, the engine drains the entire microtask queue first — every promise reaction, including ones queued by other reactions — and only then moves ONE macrotask (timer, click). This is the final piece of the event-loop machine, and the snippet on the left is the exact one interviewers use to check who really owns it.
console.log("1 sync");
setTimeout(() => {
console.log("4 macro");
}, 0);
Promise.resolve().then(() => {
console.log("3 micro");
});
console.log("2 sync");Vocabulary first. MACROTASKS: the 6.2 queue — setTimeout callbacks, clicks, network events.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Who rides where — the practical table: micro = promise reactions, queueMicrotask(fn), await-resumptions (next lesson!). Macro = setTimeout/setInterval, DOM events, network callbacks.
The drain point is precisely "each time the stack empties" — even between two macrotasks, the micro lane gets fully cleared.
Why does the language bother? Consistency guarantees. A promise reaction is guaranteed to run before anything else can happen — no click, no timer can sneak between your .thens. Chained state updates feel atomic (indivisible). The guarantee is exactly what await builds on — 6.6 will read as "microtasks with pretty syntax."
Debug-life note: this is why a log inside .then appears before a log inside a setTimeout(fn, 0) written earlier. That ordering "bug" has consumed thousands of confused hours. You now dissolve it with one sentence: different lanes.
⌨️ one of each, in the right lanes
Produce the exact output sync → micro → macro using one plain print, one promise reaction, and one zero-delay timer — written in an order that PROVES the lanes decide, not the line numbers.
requirements:
- Schedule the timer FIRST in your code: a 0ms
setTimeoutprinting"macro". - Then attach a promise reaction printing
"micro"(an already-resolved promise is fine). - Then a plain
console.log("sync")as your LAST line. Required output:sync,micro,macro— the reverse of how you wrote them.
when you press RUN, the console must show exactly:
✏️ Quick check 1
Type the FULL output order, separated by spaces:
setTimeout(() => console.log("t"), 0);
Promise.resolve().then(() => console.log("p"));
console.log("s");✏️ Quick check 2
When the stack empties, the microtask queue drains ___ before the next macrotask. Type: partially or completely.
✏️ Quick check 3
A .then reaction and a setTimeout(fn, 0) are both ready. Which runs first? Type: then or timeout.
🗣️ Now teach it back
The interviewer shows you sync + setTimeout(0) + Promise.then and asks for the output order and WHY. Give the two-queue explanation, the drain rule, and name what rides in each lane.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.