Day 1: Deep Dive into the Event Loop in Node.js
Have you ever wondered how JavaScript, despite being a single-threaded language, manages async tasks like setTimeout or fetch() without freezing? The secret lies in the Event Loop.
Today, I’m brushing up on one of the most fundamental Node.js internals that makes non-blocking I/O possible. 🚀
JavaScript is often called single-threaded. But then, how does it handle thousands of concurrent operations without freezing? The answer lies in the Event Loop. Let’s break it down step by step.
🔹 1. The Call Stack
At the core, JavaScript runs on a call stack.
Functions are pushed when called, popped when returned.
Errors show stack traces because they follow this order.
Example:
function multiply(a, b) { return a * b; }
function square(n) { return multiply(n, n); }
function printSquare(n) { console.log(square(n)); }
printSquare(4);
Execution order: printSquare → square → multiply → return → console.log.
🔹 2. The Blocking Problem
If a function takes too long (e.g., a while loop or synchronous request), it blocks the stack.
While blocked, nothing else runs.
Browser can’t repaint.
Node.js can’t handle new requests.
This is why most APIs in JS are asynchronous.
🔹 3. Async APIs Behind the Scenes
The JS engine itself doesn’t know about setTimeout, fetch, or file system calls.
In the browser, these are handled by Web APIs.
In Node.js, they’re handled by C++ APIs and libuv.
These APIs do the heavy lifting in parallel and return results later, not blocking the stack.
🔹 4. The Event Loop at Work
Here’s the process:
You call an async function (
setTimeout,fetch, I/O).That task is delegated to Web APIs (or Node’s APIs).
Once finished, they push callbacks into a queue.
The Event Loop constantly checks:
Is the stack empty?
If yes → move the first callback from the queue into the stack → execute.
Example:
console.log("Hi");
setTimeout(() => console.log("There"), 0);
console.log("Node.js");
Output:
Hi
Node.js
There
Even with setTimeout(0), the callback waits until the stack is clear.
🔹 5. Microtask Queue & Starvation
JavaScript has two queues:
Callback Queue: for timers, events, I/O.
Microtask Queue: for promises (
.then,catch) andprocess.nextTick.
Rule: Microtasks always run before callbacks.
This can cause starvation:
function loop() {
Promise.resolve().then(loop);
}
loop(); // callback queue never runs
Here, the callback queue (like setTimeout) is starved, because microtasks keep taking priority.
🔹 6. Rendering & Smoothness
The browser wants to repaint ~60 times per second (every 16.6ms).
If the stack is busy, it can’t render.
That’s why heavy loops or too many tasks → laggy UI.
In Node.js, the same blocking issue means incoming requests must wait.
🔹 7. Why Event Loop Matters for Engineers
Frontend: Don’t block the stack → smooth UIs.
Backend: Event loop + non-blocking I/O → handle thousands of connections.
System Design: Concepts like queues, tasks, and prioritization mirror real-world system design principles.
🔹Summary - Brushing Up Things
JS runs on a single call stack.
Async tasks rely on APIs outside the JS engine.
Event loop bridges the queue → stack.
Microtasks have higher priority than callbacks.
Don’t block the stack if you want scalable, responsive apps.
That’s it for today’s deep dive into the Event Loop!!.
Let’s meet tomorrow with another core Node.js concept!🚀