- Node.js Tutorial
- NodeJS Home
- NodeJS Introduction
- NodeJS Setup
- NodeJS First App
- NodeJS REPL
- NodeJS Command Line
- NodeJS NPM
- NodeJS Callbacks
- NodeJS Events
- NodeJS Event-Loop
- NodeJS Event-Emitter
- NodeJS Global-Objects
- NodeJS Console
- NodeJS Process
- NodeJS Buffers
- NodeJS Streams
- Node.js File Handling
- Node.js File System
- Node.js Read/Write File
- Working with folders in Node.js
- HTTP and Networking
- Node.js HTTP Module
- Anatomy of an HTTP Transaction
- Node.js MongoDB
- MongoDB Get Started
- MongoDB Create Database
- MongoDB Create Collection
- MongoDB Insert
- MongoDB Find
- MongoDB Query
- MongoDB Sort
- MongoDB Delete
- MongoDB Update
- MongoDB Limit
- MongoDB Join
- Node.js MySQL
- MySQL Get Started
- MySQL Create Database
- MySQL Create Table
- MySQL Insert Into
- MySQL Select From
- MySQL Where
- MySQL Order By
- MySQL Delete
- MySQL Update
- MySQL Join
- Node.js Modules
- Node.js Modules
- Node.js Built-in Modules
- Node.js Utility Modules
- Node.js Web Module
- Node.js Advanced
- Node.js Debugger
- Node.js Scaling Application
- Node.js Packaging
- Node.js Express Framework
- Node.js RESTFul API
- Node.js Useful Resources
- Node.js Useful Resources
- Node.js Discussion
Node.js Event Loop
The Event Loop is the beating heart of Node.js. It is the mechanism that allows Node.js to perform non-blocking I/O operations—despite JavaScript being single-threaded—by offloading operations to the system kernel whenever possible.
Think of the Event Loop as a tireless manager. It constantly monitors your code's execution, the call stack, and various task queues. When the call stack is empty, it picks up the next task from the queue and pushes it onto the stack for execution. This is why a Node.js server can handle thousands of concurrent connections without needing a massive amount of RAM for individual threads.
Key Features of the Event Loop
- Asynchronous Execution: Instead of waiting for a slow task (like a database query) to finish, Node.js registers a callback and moves on to the next line of code.
- Non-Blocking I/O: The system can initiate a file read or a network request and continue executing other scripts. Once the I/O is finished, the callback is sent to the event loop.
- Single Threaded: The JavaScript execution engine (V8) runs on a single main thread. This eliminates complex synchronization issues common in multi-threaded environments, like "deadlocks."
fs.readFile instead of fs.readFileSync) in production code to keep the Event Loop moving smoothly.
How the Event Loop Works
The event loop doesn't just run everything in a single pile; it organizes tasks into specific stages. Each phase has a FIFO (First-In, First-Out) queue of callbacks to execute.
1. Phases of the Event Loop
When Node.js starts, it initializes the event loop and processes the provided input script. It then enters the loop, which cycles through these phases:
- Timers: This phase executes callbacks scheduled by
setTimeout()andsetInterval()once the threshold has passed. - Pending Callbacks: Executes I/O callbacks deferred from the previous loop iteration (usually system-level errors like TCP errors).
- Idle, Prepare: Only used internally by Node.js for housekeeping.
- Poll: The most critical phase. This is where Node.js retrieves new I/O events. It will "poll" for new data and execute callbacks for nearly all I/O (except timers, close callbacks, and
setImmediate). - Check: This phase allows you to execute code immediately after the Poll phase. Callbacks from
setImmediate()are handled here. - Close Callbacks: Handles the cleanup of resources, such as
socket.on('close', ...).
process.nextTick() and Promises. These are processed between each phase of the event loop, taking priority over the main phases.
2. Example of Event Loop in Action
To understand the order of execution, consider how Node.js schedules different types of tasks:
console.log('Start');
setTimeout(() => {
console.log('Timeout 1 (1s)');
}, 1000);
setTimeout(() => {
console.log('Timeout 2 (0ms)');
}, 0);
setImmediate(() => {
console.log('Immediate');
});
console.log('End');
Output:
Start
End
Timeout 2 (0ms)
Immediate
Timeout 1 (1s)
- The synchronous
console.logcalls run immediately because they go straight to the call stack. Timeout 2andImmediateare scheduled.Timeout 2(0ms) usually executes first as the loop hits the Timers phase, followed bysetImmediatein the Check phase.Timeout 1waits for the 1000ms threshold before its callback is moved to the queue.
setTimeout(fn, 0) means the function runs exactly in 0 milliseconds. It actually means "run this as soon as possible after the current execution and the timer phase is reached."
3. Understanding Non-Blocking I/O
In a real-world web application, you might be reading a configuration file or a template. Here is how Node.js avoids getting "stuck":
const fs = require('fs');
console.log('1. Requesting file read...');
fs.readFile('large-data.json', 'utf8', (err, data) => {
if (err) return console.error(err);
console.log('3. File read successfully.');
});
console.log('2. Continuing with other tasks...');
Output:
1. Requesting file read...
2. Continuing with other tasks...
3. File read successfully.
- Node.js hands the file-reading task to the OS.
- While the disk is spinning and data is being read, the main thread is free to handle other users or calculations.
- When the file is ready, the callback is placed in the Poll phase queue and eventually executed.
4. Event Loop and Blocking Code
The "Single Thread" nature of Node.js means that if you perform a massive calculation, the Event Loop cannot move to the next phase. This is known as "Blocking the Event Loop."
console.log('Start');
// Simulating a CPU-intensive task (e.g., complex cryptography)
for (let i = 0; i < 5e9; i++) {
// Heavy math happening here
}
console.log('End');
Output:
Start
(Pause for several seconds...)
End
5. Event Loop with Promises
Promises use the Microtask Queue. This queue is emptied immediately after the current operation finishes, before the Event Loop moves to the next phase.
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => {
console.log('Promise Resolved');
});
console.log('End');
Output:
Start
End
Promise Resolved
Timeout
- The
Promise.then()callback is placed in the Microtask queue. - The Event Loop prioritizes the Microtask queue over the Timers phase, so the promise logs its output before the
setTimeoutcallback, even though both appear "ready" at the same time.
process.nextTick() if you need to ensure a piece of code runs immediately after the current function finishes, but before the event loop continues. However, use it sparingly as it can starve the I/O phases if called recursively.
Summary
The Node.js Event Loop is what makes the platform uniquely suited for high-concurrency applications like chat servers, real-time dashboards, and APIs. By understanding its phases—specifically the Poll, Check, and Timer phases—you can write more predictable and performant code. Remember the golden rule: Don't block the loop. If you have heavy computation, offload it to a Worker Thread or a separate microservice to keep your main application responsive.