The Event Loop: Microtasks vs. Macrotasks
Quick Overview
The microtask queue drains completely before a single task runs, so a `.then()` always beats a `setTimeout(fn, 0)` queued before it. A microtask that queues more microtasks starves timers entirely.
Deep Dive
1. Single-Threaded Concurrency
JavaScript is single-threaded (one Call Stack, executing one line of code at a time). Yet it handles async network requests and UI timers seamlessly using the Event Loop.
2. Why It Matters
Knowing the execution priority between Promises and setTimeout prevents subtle timing bugs in asynchronous applications.
3. Queue Priority Rules
The Event Loop continually monitors the Call Stack. When the Call Stack becomes completely empty, it processes pending callbacks in this exact order:
- Microtask Queue (Highest Priority): Promises (
.then(),async/await),queueMicrotask(). All microtasks drain completely before the loop touches macro tasks! - Task Queue / Macrotasks:
setTimeout,setInterval, I/O events, UI rendering.
[ Call Stack (Empty) ]
^
|-- 1. Drain ALL Microtasks (Promise.then)
+-- 2. Process ONE Macrotask (setTimeout)
console.log("1. Sync Start");
setTimeout(() => console.log("2. Macrotask (Timeout)"), 0);
Promise.resolve().then(() => console.log("3. Microtask (Promise)"));
console.log("4. Sync End");
// Output Order:
// 1. Sync Start
// 4. Sync End
// 3. Microtask (Promise) <- Drains BEFORE timeouts!
// 2. Macrotask (Timeout)
4. Common Mistakes
- Assuming
setTimeout(fn, 0)Runs Immediately:setTimeout(fn, 0)yields control to the Event Loop. It runs ONLY after all synchronous code and microtasks finish! - Starving the Event Loop with Endless Microtasks: Recursively queuing microtasks starves the Event Loop, permanently blocking timeouts, I/O, and UI repaints.
5. Try It Yourself
Write it yourself. The tests below run against your code.
Return Event Loop Execution Queue Order
Examples
6. Next Steps
Promises always drain before setTimeout. Next, let's dive into Promises!
Next Up in JavaScript Engine & Core Language
Data Types in JavaScript: Primitives vs. Objects
Primitives copy by value and objects copy by reference, so `b = a` duplicates a number but shares an object. That single distinction explains most surprise mutations in a codebase.
Variables: const, let & var (The Box Metaphor)
`const` prevents reassignment, not mutation: a `const` object's properties can still be changed. `let` is scoped to its block, while `var` is function-scoped and leaks out of loops.
Strings & Template Literals `${}`
Only backticks interpolate: `${user}` reads the variable, while the identical text in quotes prints literally. Strings are immutable, so every method returns a new one rather than editing in place.