June 23, 2025
Understanding the JavaScript Event Loop 🔁


Explore the interactive demo to visualize how the event loop works in real-time. ⭐ the repo if you find it useful!
Introduction
JavaScript is single-threaded, meaning it executes one piece of code at a time. Yet, it handles asynchronous tasks like network requests, timers, and DOM events seamlessly. The secret sauce? The Event Loop.
Core Components
- Call Stack: A LIFO stack of function frames being executed.
- Web APIs (or Host APIs): Browser or Node.js-provided APIs (e.g.,
setTimeout
, DOM events,fetch
). - Task Queues:
- Macrotask Queue: e.g.,
setTimeout
,setInterval
, I/O tasks - Microtask Queue: e.g.,
Promise
callbacks (.then
,catch
,finally
),MutationObserver
- Macrotask Queue: e.g.,
- Event Loop: Continuously checks the call stack and task queues to determine what to execute next.
How It Works
- Execute Script: Synchronous code runs, pushing and popping frames on the call stack.
- Register Async Tasks: When encountering an async API (e.g.,
setTimeout
), it’s handed off to Web APIs. - Queue Callbacks: Upon completion, callbacks are queued:
- Microtasks are queued first (higher priority).
- Macrotasks are queued next.
- Drain Microtasks: After each task completes and the call stack is empty, the event loop drains all microtasks before processing the next macrotask.
- Process Next Macrotask: The loop picks the next macrotask and repeats.
Code Examples
Example 1: setTimeout vs Promise
console.log("Start");
setTimeout(() => {
console.log("Timeout callback");
}, 0);
Promise.resolve().then(() => {
console.log("Promise callback");
});
console.log("End");
Expected Output:
Start
End
Promise callback
Timeout callback
Why? Promises (microtasks) run before setTimeout
(macrotasks), even with a 0ms delay.
Example 2: Nested Tasks
console.log("A");
setTimeout(() => {
console.log("B");
Promise.resolve().then(() => {
console.log("C");
});
}, 0);
Promise.resolve().then(() => {
console.log("D");
setTimeout(() => {
console.log("E");
}, 0);
});
console.log("F");
Output:
A
F
D
B
C
E
Visualizing the Loop

- Start: Synchronous logs push/pop on the stack.
- Promise.then: Queued in microtask queue, drained before any macrotask.
- setTimeout: Queued in macrotask queue, executed after microtasks.
Common Pitfalls
- Long-running tasks block the event loop, freezing the UI.
- Excessive microtasks starvation can delay macrotasks.
- setTimeout(fn, 0) is not “immediate”; it’s scheduled as a macrotask.
Best Practices
- Use microtasks (
Promise
,queueMicrotask
) for short, high-priority callbacks. - Use macrotasks (
setTimeout
,setInterval
) for tasks that can be deferred. - Avoid heavy computation on the main thread; leverage Web Workers or offload to a server.
Conclusion
The JavaScript Event Loop is the heart of asynchronous behavior in JS. Understanding its mechanics empowers you to write efficient, non-blocking code and debug tricky timing issues.
Happy looping! 🔄🚀