Understanding libuv and event loop
What is Libuv ?

Software Developer
Libuv is a library written in the programming language C that helps nodejs to improve efficiency while running tasks parallelly. However, nodejs already have async API's. It uses Libuvs's thread pools if async API is not available in nodejs and processes are blocking the operations.
Libuv doesn't perform the task itself, it only manages the operations.
Event-driven asynchronous I/O model is integrated.
It allows the CPU and other resources to be used simultaneously while still performing I/O operations, thereby resulting in efficient use of resources and network.
It facilitates an event-driven approach wherein I/O and other activities are performed using callback-based notifications.
The event loop in LIBUV operates in four major phases:
Timers Phase: In this phase, all callbacks that were set using setTimeout or setInterval are executed. These timers are checked, and if their time has expired, their corresponding callbacks are added to the callback queue for execution.
console.log("1. Start (sync)");
setTimeout(() => {
console.log("TIMERS phase (setTimeout 0ms)");
}, 0);
setTimeout(() => {
console.log("TIMERS phase (setTimeout 10ms)");
}, 10);
console.log("2. End (sync)");
Start (sync)
End (sync)
TIMERS phase (setTimeout 0ms)
TIMERS phase (setTimeout 10ms)
Poll Phase: After timers, the event loop enters the Poll phase, which is crucial because it handles I/O callbacks. For instance, when you perform a file read operation using fs.readFile , the callback associated with this I/O operation will be executed in this phase. The Poll phase is responsible for handling all I/O related tasks, making it one of the most important phases in the event loop.
const fs = require("fs");
console.log("1. Start (sync)");
fs.readFile(__filename, "utf-8", (err, data) => {
console.log("POLL phase (fs.readFile callback)");
});
setTimeout(() => {
console.log("TIMERS phase");
}, 0);
console.log("2. End (sync)");
Start (sync)
End (sync)
TIMERS phase
POLL phase (fs.readFile callback)
Check Phase: Next is the Check phase, where callbacks scheduled by the setImmediate function are executed. This utility API allows you to execute callbacks immediately after the Poll phase, giving you more control over the order of operations.
console.log("1. Start (sync)");
setImmediate(() => {
console.log("CHECK phase (setImmediate)");
});
setTimeout(() => {
console.log("TIMERS phase (setTimeout 0)");
}, 0);
console.log("2. End (sync)");
Start (sync)
End (sync)
CHECK phase (setImmediate)
TIMERS phase (setTimeout 0)
Close Callbacks Phase: Finally, in the Close Callbacks phase, any callbacks associated with closing operations, such as socket closures, are handled. This phase is typically used for cleanup tasks, ensuring that resources are properly released.
const { createServer } = require("http");
console.log("1. Start (sync)");
const server = createServer();
server.on("close", () => {
console.log("CLOSE callbacks phase (server 'close' event)");
});
server.listen(0, "127.0.0.1", () => {
console.log("Server listening");
server.close();
});
setImmediate(() => {
console.log("CHECK phase");
});
console.log("2. End (sync)");
Start (sync)
End (sync)
Server listening
CHECK phase
CLOSE callbacks phase (server 'close' event)
NOTE : When the event loop is empty and there are no more tasks to execute, it enters the poll phase and essentially waits for incoming events.
Thread pool in libuv :
Whenever there's an asynchronous task, V8 offloads it to libuv. For example, when reading a file, libuv uses one of the threads in its thread pool. The file system (fs) call is assigned to a thread in the pool, and that thread makes a request to the OS. While the file is being read, the thread in the pool is fully occupied and cannot perform any other tasks. Once the file reading is complete, the engaged thread is freed up and becomes available for other operations. For instance, if you're performing a cryptographic operation like hashing, it will be assigned to another thread. There are certain functions for which libuv uses the thread pool.
In Node.js, the default size of the thread pool is 4 threads:
UV_THREADPOOL_SIZE=4
Suppose you have a server with many incoming requests, and users are hitting APIs. Do these APIs use the thread pool?
In the libuv library, when it interacts with the OS for networking tasks, it uses sockets. Networking operations occur through these sockets. Each socket has a socket descriptor, also known as a file descriptor (although this has nothing to do with the file system).
When an incoming request arrives on a socket, and you want to write data to this connection, it involves blocking operations. To handle this, a thread is created for each request. However, creating a separate thread for each connection is not practical, especially when dealing with thousands of requests.
Instead, the system uses efficient mechanisms provided by the OS, such as epoll (on Linux) or kqueue on macOS. These mechanisms handle multiple file descriptors (sockets) without needing a thread per connection:
Here How it works:
epoll(Linux) andkqueue(macOS) are notification mechanisms used to manage many connections efficiently.When you create an
epollorkqueuedescriptor, it monitors multiple file descriptors (sockets) for activity.The OS kernel manages these mechanisms and notifies libuv of any changes or activity on the sockets.
This approach allows the server to handle a large number of connections efficiently without creating a thread for each one.
The kernel-level mechanisms, like epoll and kqueue , provide a scalable way to manage multiple connections, significantly improving performance and resource utilization in a high-concurrency environment.
Important points :
DON'T BLOCK THE MAIN THREAD
Don't use
syncmethodsDon't do operations on
heavy JSON Objectit will make load on main thread.Avoid complex
Regular Expression.Avoid
Complex calculationsand big orinfine loops.
Data Structures is important
epoll- Red Balck treetimers-min heap



