Why we need this knowledge?
Because understanding the event loop helps you write non-blocking, scalable NodeJS applications, debug async issues confidently, and stand out as a real backend engineer instead of just “someone who uses async/await”.
Introduction
The event loop in NodeJS handles asynchronous code execution. It operates in a cycle, moving through a series of phases, and allows NodeJS to perform non-blocking I/O operations efficiently.
Main things involved in the event loop:
- Microtasks
- Phases
- Macrotasks
NodeJS uses libuv to implement an event-driven, non-blocking I/O model.
JavaScript runs on a single main thread (call stack), but behind the scenes NodeJS uses a multi-threaded C++ library (libuv) to handle heavy operations.
libuv and NodeJS
libuv is a C library that provides:
- Event loop
- Thread pool
- Asynchronous I/O
- Networking support
avaScript runs on a single main thread (V8 engine), but libuv enables NodeJS to handle massive concurrency efficiently.
Thread Pool
The thread pool is used for CPU-intensive or blocking tasks such as:
- File system operations
- DNS lookup
- Some cryptographic functions
These tasks are offloaded to the thread pool so the main thread can continue processing other requests without blocking.
How libuv Works with NodeJS
libuv allows NodeJS to handle a high volume of concurrent connections efficiently.
1. JavaScript Execution
The V8 engine executes JavaScript code on a single main thread using the call stack.
2. Offloading Tasks
When an async operation is encountered (like fs.readFile or a network request), V8 delegates the task to libuv.
3. Asynchronous Handling
Network I/O For network operations like TCP sockets or HTTP requests, libuv uses the operating system’s native non-blocking mechanisms. The OS handles communication in the background.
File I/O and CPU-intensive Tasks For file system access or heavy computation that is blocking, libuv assigns the task to an available worker thread in its internal thread pool.
4. Callback Queuing
Once the operation is completed (either by the OS or worker thread), libuv places the associated callback function into the event loop queue.
5. Callback Execution
When the main JavaScript call stack is empty, the event loop picks the callback from the queue and executes it on the main thread.
By coordinating async operations, using a non-blocking I/O model, and leveraging a multi-threaded architecture behind the scenes, NodeJS becomes a powerhouse for building scalable network applications.
Phases of the Event Loop (libuv phases)
NodeJS event loop runs through these phases in order:
- Timers Executes setTimeout and setInterval callbacks.
- I/O Callbacks Executes callbacks for some pending async operations.
- Idle / Prepare Internal use (NodeJS housekeeping).
- Poll Retrieves new I/O events and executes I/O callbacks.
- Check Executes setImmediate() callbacks.
- Close Callbacks Executes close events like socket.close().
Microtask Queue vs Macrotask Queue
Microtask Queue
- Not part of event loop phases
- Highest priority queue
- Examples: Promises (then, catch) process.nextTick()
- Promises (then, catch)
- process.nextTick()
- Executed after every callback and after every phase.
Macrotask Queue / Callback Queue
- Lower priority than microtask queue
- Examples: Timers (setTimeout, setInterval) I/O callbacks Browser APIs
- Timers (setTimeout, setInterval)
- I/O callbacks
- Browser APIs
Simple Example: Execution Timeline
console.log("start");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
setImmediate(() => console.log("immediate"));
fs.readFile("sample.txt", () => console.log("file"));
console.log("end");
Output:
start
end
promise
timeout
file
immediate

Note:
NodeJS runs JavaScript on a single main thread, but libuv’s C++ thread pool handles I/O operations in the background, enabling massive concurrency without blocking the event loop.



