libuv and Non-Blocking I/O
Understand the native eventing and thread-pool layer beneath Node.js asynchronous APIs.
Lesson content
🎯 Goal By the end of this lesson, you'll understand: What is libuv ? What is Non-Blocking I/O ? Why does Node.js use libuv? How libuv works Real-world examples Interview questions What is libuv? libuv is a C library used internally by Node.js. It provides: Event Loop Thread Pool Asynchronous File System Networking Timers Simple Definition: libuv is the engine behind Node.js that handles asynchronous operations. Why Do We Need libuv? JavaScript runs on one thread . If JavaScript handled slow tasks itself, the entire application would stop. Instead, Node.js gives those tasks to libuv . Real-Life Example 👨🍳 Imagine a chef in a restaurant. Chef │ ▼ Take Order │ ▼ Give Cooking to Kitchen Staff │ ▼ Take Next Order │ ▼ Food Ready │ ▼ Serve Customer The chef doesn't cook every dish personally. Similarly, JavaScript = Chef 👨🍳 libuv = Kitchen Staff 🍳 What is Non-Blocking I/O? Non-Blocking I/O means the program doesn't wait for slow operations like: Reading files Database queries API calls Network requests Instead, it continues executing other code. Simple Definition: Start the task, continue working, and handle the result later. Blocking vs Non-Blocking Blocking I/O const fs = require ( "fs" ); const data = fs . readFileSync( "test.txt" , "utf8" ); console . log( data ); console . log( "Done" ); Output (File Content) Done Execution Read File │ Wait... │ Print File │ Done Everything waits. Non-Blocking I/O const fs = require ( "fs" ); fs . readFile( "test.txt" , "utf8" , ( err , data ) => { console . log( data ); }); console . log( "Done" ); Output Done (File Content) Execution Read File │ ▼ libuv │ ▼ Continue Program │ ▼ Done │ ▼ File Ready │ ▼ Callback Executes How libuv Works JavaScript (Main Thread) │ ▼ Calls fs.readFile() │ ▼ libuv (Thread Pool / OS) │ ▼ Reads File │ ▼ Callback Queue │ ▼ Event Loop │ ▼ Call Stack Thread Pool libuv has a Thread Pool (default size: 4 threads ) for tasks like: File System Operations DNS Lookup Compression Encryption libuv Thread Pool Thread 1 → File A Thread 2 → File B Thread 3 → File C Thread 4 → File D Note: The thread pool size can be changed using the UV_THREADPOOL_SIZE environment variable (up to a supported limit). Example const fs = require ( "fs" ); console . log( "Start" ); fs . readFile( "a.txt" , () => { console . log( "File A" ); }); fs . readFile( "b.txt" , () => { console . log( "File B" ); }); console . log( "End" ); Output Start End File A File B The file reads happen asynchronously. Why is Non-Blocking I/O Fast? Imagine 1,000 users requesting data. Blocking User 1 → Wait User 2 → Wait User 3 → Wait Everyone waits. Non-Blocking User 1 ─┐ User 2 ─┼──► libuv User 3 ─┘ Responses return independently. The server remains responsive. Operations Handled by libuv ✅ File Reading/Writing ✅ DNS Lookup ✅ Timers ✅ TCP/UDP Networking ✅ Pipes ✅ Child Processes Operations NOT Handled by libuv Thread Pool Simple JavaScript operations run directly on the main thread. console . log( 10 + 20 ); let total = 0 ; for ( let i = 0 ; i < 100 ; i ++ ) { total += i ; } These execute synchronously. libuv vs JavaScript JavaScript libuv Executes application code Handles asynchronous operations Single thread Uses Event Loop and thread pool where needed Runs synchronous code Manages I/O tasks Real-Life Example 🏦 Bank Counter Customer │ ▼ Submit Form │ ▼ Back Office Processes It │ ▼ Customer Waits │ ▼ Continue Serving Others │ ▼ Result Ready The back office is like libuv . Interview Question What is the output? console . log( "Start" ); const fs = require ( "fs" ); fs . readFile( "test.txt" , () => { console . log( "File Loaded" ); }); console . log( "End" ); Output Start End File Loaded Reason fs.readFile() is asynchronous. libuv handles the file reading while JavaScript continues executing the remaining code. Best Practices ✅ Use asynchronous APIs ( fs.readFile , fs.writeFile ) instead of synchronous ones in servers. ✅ Keep the Event Loop free by avoiding long-running synchronous tasks. ✅ Use Worker Threads for CPU-intensive work instead of blocking the main thread. ❌ Avoid readFileSync() in production request handlers because it blocks the Event Loop. Key Points ✅ libuv is the C library that powers Node.js asynchronous operations. ✅ libuv provides the Event Loop, thread pool, timers, and networking support. ✅ Non-Blocking I/O allows Node.js to continue executing code while waiting for slow operations. ✅ File system operations, DNS lookups, and some other tasks use libuv's thread pool. ✅ Non-blocking I/O is one of the main reasons Node.js performs well for I/O-heavy applications. 🧠 Easy Formula Need File/API/Database? │ ▼ JavaScript │ ▼ libuv │ ▼ Background Work │ ▼ Event Loop │ ▼ Callback Runs Blocking I/O Wait → Execute Non-Blocking I/O Start → Continue → Finish Later Remember: ⚙️ libuv = Engine behind Node.js asynchronous operations 🚀 Non-Blocking I/O = Don't wait, continue working 📂 libuv keeps the Event Loop responsive by handling slow I/O operations in the background.