Understanding the javascript event loop
If you’ve ever wondered how JavaScript manages to handle multiple things seemingly at once — from fetching APIs to responding to clicks — while being single-threaded, the answer lies in the Event Loop.
zlaam
🌀 Understanding the JavaScript Event Loop: The Heartbeat of Concurrency
If you’ve ever wondered how JavaScript manages to handle multiple things seemingly at once — from fetching APIs to responding to clicks — while being single-threaded, the answer lies in the Event Loop.
This mechanism is what gives JavaScript its asynchronous superpower. Let’s dive deep and understand how it actually works.
🧠 The Core Idea
JavaScript runs on a single thread, meaning it can execute one statement at a time.
So, how does it deal with tasks that take time — like network requests or timers — without freezing the entire UI?
Enter the Event Loop, which acts as a traffic controller managing synchronous and asynchronous operations.
⚙️ The Components
Before understanding the loop itself, you need to know the ecosystem it manages:
1. Call Stack
- It’s a stack data structure.
- Every function call is pushed onto it.
- When the function finishes, it’s popped off.
Example:
function first() {
second();
console.log("First");
}
function second() {
console.log("Second");
}
first();
Order of execution:
Call Stack:
1. first()
2. second()
3. console.log("Second")
4. console.log("First")
2. Web APIs (or Node APIs in Node.js)
When you use things like:
setTimeout()fetch()addEventListener()
These aren’t part of JavaScript itself — they are provided by the browser environment (or Node runtime).
They handle the background work while JavaScript continues executing other tasks.
3. Callback Queue (or Task Queue)
When an async task (like a setTimeout) finishes, its callback is sent here — waiting for the call stack to become empty.
4. Microtask Queue
This queue is special and has higher priority than the callback queue.
Promises (.then, .catch, .finally) and MutationObserver callbacks go here.
5. The Event Loop Itself
Now comes the magic part.
The event loop continuously checks:
- If the call stack is empty.
- If yes, it takes the first task from the microtask queue, executes it.
- Once the microtask queue is empty, it moves to the callback queue.
This cycle repeats forever — hence, the loop.
⏳ Example: Timers vs Promises
Let’s test your intuition.
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");
Output:
Start
End
Promise
Timeout
Why?
console.log("Start")andconsole.log("End")run synchronously.setTimeout(..., 0)goes to the callback queue.Promise.resolve()goes to the microtask queue.- After the main stack is clear:
- Event loop checks microtask queue first → logs “Promise”.
- Then moves to callback queue → logs “Timeout”.
🧩 Visualizing It
┌──────────────────────────────┐
│ Call Stack │
└─────────────┬────────────────┘
│
▼
┌────────────────────┐
│ Event Loop │
└───────┬─────────────┘
│
┌────────┴────────┐
│ Microtask Queue │ ← (Promises)
│ Callback Queue │ ← (Timers, Events)
└──────────────────┘
⚡ Microtasks Can Block Too!
If you keep adding microtasks endlessly, you can starve the callback queue.
function loop() {
Promise.resolve().then(loop);
}
loop();
This will never let any setTimeout callback run — because the event loop is always busy with microtasks.
🧩 In Node.js
Node.js has a similar but slightly different mechanism. It has:
- Microtask queue (same behavior)
- Phases for timers, I/O, check, close callbacks, etc.
In short, Promises still have priority over setTimeout, but Node adds its own phases internally for efficiency.
🧠 Key Takeaways
- JavaScript is single-threaded, but non-blocking.
- Web APIs handle async work in parallel.
- Event Loop coordinates between:
- Call Stack
- Microtask Queue
- Callback Queue
- Promises always resolve before timers.
- Infinite microtasks can block your main thread.
🧵 Final Thought
Understanding the Event Loop transforms how you reason about concurrency in JavaScript.
It’s not magic — just a clever orchestration of queues, stacks, and timing.
Next time your async code “behaves weirdly”, peek inside the loop — that’s where the answer lies.
Would you like me to make this blog SEO-optimized (with meta tags, headings, and summary) so you can publish it directly on your site (like Astro, Next.js, or Hashnode)?
zlaam
Author
Comments (1)
You need to be logged in to post comments
Nice
Thank you