Node.js Event Loop & Core Concepts
This guide provides a foundational understanding of how Node.js works, its architecture, and how it handles asynchronous operations.1. Introduction to Node.js
Node.js is a JavaScript Runtime Environment that allows JavaScript to run outside the browser. It is built by wrapping the Google V8 engine and the Libuv library to enable server-side development. Core Principle:Node.js is a SINGLE-THREADED environment While JavaScript execution happens on a single thread, Node.js offloads complex tasks to the operating system or its internal thread pool.Primary Use Cases:
- I/O Operations: Reading/Writing files, Database calls.
- Real-time Applications: Chat apps, Game servers.
- REST APIs: Fast, scalable web services.
2. Event Loop Architecture
The Event Loop is the “heart” of Node.js. It monitors for pending tasks and manages asynchronous operations, enabling non-blocking I/O.How Node.js Reads Code
- Top-to-bottom: Executes synchronous code immediately.
- Delegation: Asynchronous tasks (like timers or network calls) are sent to the Event Loop.
- Callback Queue: Once a task is done, its callback is queued and processed when the main thread is free.
3. Blocking vs Non-Blocking
Blocking Code Example
Blocking Code Example
Blocking: The thread is “locked” until the operation completes. No other code can run.
Non-Blocking Code Example
Non-Blocking Code Example
Non-Blocking: The operation is delegated. The thread continues working on other things.
4. Event Loop Phases & Priority
The Event Loop executes tasks in a specific hierarchical order:| Phase | Priority | Tasks |
|---|---|---|
| Microtask Queue | Highest | process.nextTick(), Promise.resolve() |
| Timers | 1st Macrotask | setTimeout(), setInterval() |
| I/O Callbacks | 2nd Macrotask | Network / File System results |
| Poll | 3rd Macrotask | New I/O events |
| Check | 4th Macrotask | setImmediate() |
| Close | 5th Macrotask | Socket closures, cleanup |
5. CPU Intensive Tasks & Multi-Threading
Because of its single-threaded nature, CPU-intensive tasks (like heavy loops) can block the entire server, making it unresponsive to other requests.Solution 1: Child Processes (fork)
Creates a separate instance of Node.js with its own memory. Good for isolated scripts.
Solution 2: Worker Threads
Threads that share memory with the main process. More lightweight and efficient for mathematical calculations.Thread Pool
Node.js uses Libuv to maintain a thread pool (default size: 4) for operations like file I/O, hash generation (crypto), and compression (zlib).
6. Streams & Buffers
Processing large data efficiently.| Feature | Stream | Buffer |
|---|---|---|
| Mechanism | Processes data in chunks | Loads everything into memory |
| Memory | Efficient (low footprint) | High (can crash on large files) |
| Use Case | Video, big logs, file uploads | Small, fixed-size data |
7. Rate Limiting
Crucial for protecting your server from DDoS attacks and Resource Exhaustion. Implementation: Use libraries likeexpress-rate-limit to restrict requests per IP address.
8. Event-Driven Architecture
Using theEventEmitter to create decoupled, scalable applications.
9. Common Interview Questions
Q1: Difference between setImmediate() and process.nextTick()?
Q1: Difference between setImmediate() and process.nextTick()?
Answer:
process.nextTick()is a Microtask. It runs immediately after the current operation and before any other phase of the event loop.setImmediate()is a Macrotask. It runs in the “Check” phase of the event loop. Warning: Excessive nextTick can cause “starvation” of the event loop.
Q2: What is the Thread Pool in Node.js?
Q2: What is the Thread Pool in Node.js?
Answer:
Node.js uses Libuv to manage a pool of background threads. While the event loop handles networking, the thread pool is used for:
- File System operations (
fs) - Cryptography (
crypto) - DNS lookups
- Compression (
zlib)
Q3: fork() vs spawn()?
Q3: fork() vs spawn()?
Answer:
fork(): Specialized for Node.js. Creates a new Node engine instance and an IPC channel for easy communication.spawn(): General purpose. Used to run any system command (likepythonorgit) as a separate process.
Q4: Why is Node.js not ideal for heavy computation?
Q4: Why is Node.js not ideal for heavy computation?
Answer:
Since it’s single-threaded, a single heavy computation (e.g., image resizing) will block the thread, making the server unresponsive for all other users. For such tasks, it’s better to use Worker Threads or offload the work to a specialized service (like a Python microservice).