JavaScript Event Loop: The Ultimate Guide (2025 Edition)
A clear, modern explanation of the event loop, microtasks, macrotasks, timers, promises, and how Node.js and browsers differ.
1. Why the Event Loop Exists
JavaScript runs in a single thread — meaning it can execute only one piece of code at a time.
But real applications need to:
- Fetch data from APIs
- Handle user events
- Read files
- Run timers
- Animate the UI
Executing these synchronously would block the entire app.
➡️ The event loop allows JavaScript to be non-blocking and asynchronous despite being single-threaded.
2. Big Picture Architecture
A JavaScript runtime (Browser or Node.js) includes:
- Call Stack
- Heap (Memory)
- Web APIs / Node APIs
- Macrotask Queue (Task Queue)
- Microtask Queue
- Event Loop
High-level flow:
JS Code → Call Stack → (async APIs) → Microtasks / Macrotasks
↑
Event LoopThe Event Loop checks:
“Is the call stack empty?”
If yes → pushes the next task.
3. Synchronous vs Asynchronous Execution
Synchronous
Runs immediately on the call stack:
console.log("A");
console.log("B");Output:
A
BAsynchronous
offloaded to the environment:
setTimeout(() => console.log("C"), 0);
console.log("D");Output:
D
C4. Macrotasks vs Microtasks
JavaScript has two task queues.
4.1 Macrotasks
Examples:
setTimeoutsetInterval- DOM events
- I/O callbacks
- setImmediate (Node.js)
- MessageChannel
4.2 Microtasks
Examples:
Promise.then()async/awaitqueueMicrotask()- MutationObserver
Execution Order
- Execute one macrotask
- Execute all microtasks
- Re-render (browser)
- Repeat
5. The Golden Rule
Promise callbacks run before setTimeout callbacks.
Example:
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");Output:
A
D
C
BBecause:
- Microtasks (Promise) run before macrotasks (setTimeout)
6. queueMicrotask()
queueMicrotask() forces a function into the microtask queue.
queueMicrotask(() => console.log("M"));
setTimeout(() => console.log("T"), 0);
console.log("S");Output:
S
M
TUsed heavily in custom Promise implementations.
7. Browser vs Node.js Event Loop
Browser Event Loop
- After each macrotask → run all microtasks → re-render UI
Node.js Event Loop (libuv)
Node runs tasks in 6 phases:
- Timers → setTimeout, setInterval
- Pending Callbacks
- Idle/Prepare
- Poll → I/O events
- Check → setImmediate
- Close Callbacks
Microtasks run:
- After each callback
- Between phases
Ordering Example (Node.js)
setTimeout(() => console.log("timeout"));
setImmediate(() => console.log("immediate"));May output either:
immediate
timeoutor the opposite.
Inside I/O:
fs.readFile("file.txt", () => {
setTimeout(() => console.log("timeout"));
setImmediate(() => console.log("immediate"));
});Always outputs:
immediate
timeout8. Visual Diagram
┌───────────────────────────┐
│ Call Stack │
└─────────────┬─────────────┘
│
↓
┌───────────────────────────┐
│ Web/Node APIs │
└─────────────┬─────────────┘
│
┌───────┴────────┐
↓ ↓
┌────────────┐ ┌──────────────┐
│ Microtasks │ │ Macrotasks │
└────────────┘ └──────────────┘
↑ │
└───────┬────────┘
↓
┌──────────────┐
│ Event Loop │
└──────────────┘9. async/await and the Event Loop
async/await uses Promises under the hood.
async function run() {
console.log("1");
await null;
console.log("2");
}
run();
console.log("3");Output:
1
3
time micro tasks -> 2Because await schedules a microtask.
10. Why UI Freezes (Browser)
while (true) {} // Infinite loopThe event loop never becomes free to process:
- Clicks
- Scroll events
- Rendering
Thus the browser “hangs”.
11. Event Loop Interview Tips
- Mention single-threaded JS
- Explain microtasks vs macrotasks
- Promise → microtask
- setTimeout → macrotask
- Node.js has 6 phases
- Browser renders after microtasks
Common interview trick:
Why does Promise resolve run before setTimeout?
Answer:
Because microtasks always run before the next macrotask.
12. Final Summary
| Concept | Meaning |
|---|---|
| Event Loop | Controls async execution |
| Microtasks | Higher priority (Promises) |
| Macrotasks | Lower priority (Timers, I/O) |
| Browser Loop | Microtasks → render → next task |
| Node Loop | 6-phase libuv loop |