The event loop is a fundamental part of Node.js architecture. It is responsible for handling asynchronous operations such as I/O tasks, timers, and callbacks. The event loop allows Node.js to perform non-blocking I/O operations despite being single-threaded. Understanding the event loop is critical to writing efficient, performant, and scalable applications in Node.js.
In this document, we will explore the concept of the Node.js event loop in detail. We will examine how it works, its phases, tasks involved, and how asynchronous code is managed using the loop. Weβll also cover key APIs like setTimeout, setImmediate, process.nextTick, and how they interact with the event loop.
The event loop is a mechanism that enables Node.js to perform non-blocking I/O operations by offloading operations to the system kernel whenever possible. Since most modern kernels are multithreaded, they can handle multiple operations in the background. Once an operation is complete, the kernel signals Node.js to continue with the callback.
Node.js uses a single-threaded model for executing JavaScript code. Without the event loop, Node.js would be unable to handle multiple operations concurrently, making it unsuitable for real-world server applications.
The event loop ensures that:
The event loop continuously runs in a loop and processes various types of callbacks in different phases. It picks callbacks from different queues and executes them based on timing and priority.
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
Output:
Start
End
Promise
Timeout
Even though `setTimeout` has a 0ms delay, the `Promise` callback runs before it due to microtask vs. macrotask prioritization.
Each iteration of the event loop is called a "tick". Each tick has multiple phases, and each phase has a queue of operations to execute.
Event Loop Cycle:
ββββββββββββββββββββββββββββ
β timers β
ββββββββββββββββββββββββββββ€
β pending callbacks β
ββββββββββββββββββββββββββββ€
β idle, prepare β
ββββββββββββββββββββββββββββ€
β poll β
ββββββββββββββββββββββββββββ€
β check β
ββββββββββββββββββββββββββββ€
β close callbacks β
ββββββββββββββββββββββββββββ
Executes code after a specified delay in milliseconds.
setTimeout(() => {
console.log('Executed after timeout');
}, 0);
Executes code in the check phase of the next event loop iteration.
setImmediate(() => {
console.log('Executed immediately');
});
Executes code after the current operation but before the event loop continues. Itβs part of the microtask queue.
process.nextTick(() => {
console.log('Next tick callback');
});
console.log('Start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
});
process.nextTick(() => {
console.log('nextTick');
});
console.log('End');
Expected Output:
Start
End
nextTick
setTimeout
setImmediate
Executed immediately after the current task, before the event loop continues. Examples:
Scheduled for future event loop iterations. Examples:
const fs = require('fs');
console.log('Start');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('File read complete');
});
console.log('End');
Output:
Start
End
File read complete
The file is read asynchronously. The callback executes in the poll phase after I/O completes.
const fs = require('fs');
console.log('Start');
const data = fs.readFileSync('example.txt', 'utf8');
console.log(data);
console.log('End');
fs.readFile('example.txt', 'utf8', (err, data) => {
console.log(data);
});
Node.js introduced worker threads to allow running JavaScript in parallel. Each worker has its own event loop.
const { Worker } = require('worker_threads');
const worker = new Worker(`
setTimeout(() => {
postMessage('Hello from worker!');
}, 1000);
`, { eval: true });
worker.on('message', msg => console.log(msg));
Although you can specify a delay with setTimeout, the actual execution time depends on the event loop state.
setTimeout(() => {
console.log('Executed later');
}, 10);
If the event loop is busy, the callback may be delayed beyond 10 milliseconds.
CPU-bound tasks can block the event loop, making your app unresponsive.
function heavyTask() {
for (let i = 0; i < 1e9; i++) {}
}
heavyTask(); // Blocks entire app
function heavyTask() {
let i = 0;
function processChunk() {
for (let j = 0; j < 1e5; j++) {
i++;
}
if (i < 1e9) {
setImmediate(processChunk);
}
}
processChunk();
}
node --inspect-brk script.js
The event loop is the heart of Node.js and enables its non-blocking, asynchronous nature. It handles different types of operations and schedules them efficiently in multiple phases. Understanding the event loopβs internalsβincluding how timers, I/O, microtasks, and callbacks workβallows developers to write better-performing applications and avoid common pitfalls like callback hell or blocked threads.
Key concepts to remember include:
Mastering the event loop empowers developers to take full advantage of Node.jsβs strengths in building fast, scalable, real-time applications.
A function passed as an argument and executed later.
Runs multiple instances to utilize multi-core systems.
Reusable blocks of code, exported and imported using require() or import.
nextTick() executes before setImmediate() in the event loop.
Starts a server and listens on specified port.
Node Package Manager β installs, manages, and shares JavaScript packages.
A minimal and flexible web application framework for Node.js.
A stream handles reading or writing data continuously.
It processes asynchronous callbacks and non-blocking I/O operations efficiently.
Node.js is a JavaScript runtime built on Chrome's V8 engine for server-side scripting.
An object representing the eventual completion or failure of an asynchronous operation.
require is CommonJS; import is ES6 syntax (requires transpilation or newer versions).
Use module.exports or exports.functionName.
Variables stored outside the code for configuration, accessed using process.env.
MongoDB, often used with Mongoose for schema management.
Describes project details and manages dependencies and scripts.
Synchronous blocks execution; asynchronous runs in background without blocking.
Allows or restricts resources shared between different origins.
Use try-catch, error events, or middleware for error handling.
Provides file system-related operations like read, write, delete.
Using event-driven architecture and non-blocking I/O.
Functions in Express that execute during request-response cycle.
A set of routes or endpoints to interact with server logic or databases.
Yes, it's single-threaded but handles concurrency using the event loop and asynchronous callbacks.
Middleware to parse incoming request bodies, like JSON or form data.
Copyrights © 2024 letsupdateskills All rights reserved