System theme

Node.js Processes and Their Life Cycle

Understand Node.js processes, threads, and the event loop life cycle to build scalable, non-blocking applications.

KK133 min read

Node.js Processes and Their Life Cycle

Node.js applications run as a single process by default, but they leverage multiple internal mechanisms to handle concurrent work efficiently. Understanding how processes, threads, and the event loop interact is essential for building scalable, non-blocking applications.

Node.js Process Architecture

Node.js uses a single-threaded event loop with auxiliary threads for specific tasks. This design allows it to handle thousands of concurrent connections without the complexity of multiple processes.

Node.js ProcessMain ThreadEvent LoopThread PoolWorker ThreadExecute JavaScriptI/O OperationsWorker Thread TasksCompleted I/OInitialization
Node.js Architecture with Event Loop and Worker Threads

The diagram above shows the core architecture. The main thread runs the JavaScript code, the event loop coordinates all operations, and the libuv thread pool handles blocking I/O operations in parallel.

The Event Loop Life Cycle

The event loop continuously runs in phases, processing work as it becomes available. Each cycle maintains a specific order to ensure predictable behavior.

TimersI/O CallbacksIdle/PreparePollCheckClose CallbacksCleanupDeferred I/ONew I/O EventsImmediate CallbacksRegular I/OFinal Cleanup
Event Loop Execution Order

The event loop follows these phases in sequence:

1. Timers Phase

Node.js first checks for setTimeout and setInterval callbacks that have expired.

2. Pending Callbacks Phase

Handles callbacks deferred from the previous phase's I/O operations.

3. Idle/Prepare Phase

Reserved for internal Node.js operations and preparation.

4. Poll Phase

The longest-running phase where new I/O events are retrieved and executed. This is where most application work happens.

5. Check Phase

Processes setImmediate callbacks that have been added to the queue.

6. Close Callbacks Phase

Executes cleanup callbacks for sockets and other closeable resources.

After completing a full cycle, the event loop starts again with the Timers phase.

Worker Threads Explained

Worker threads allow Node.js to perform CPU-intensive work in parallel without blocking the main event loop. They're ideal for tasks that would otherwise stall the entire application.

Main ThreadEvent LoopClient RequestWorker Thread 1Worker Thread 2Receive RequestForward CPU WorkForward CPU WorkContinue Handling I/OResult 1Result 2Send Response

Worker threads share memory with the main thread but run in separate V8 isolates, preventing garbage collection issues and ensuring isolation.

Process Lifecycle Management

A Node.js application follows a predictable lifecycle from startup to shutdown:

Startup Phase

  1. Module Loading - All required modules are loaded and cached
  2. Entry Point Execution - The main application code begins running
  3. Event Loop Initialization - The event loop is prepared to handle events

Running Phase

  1. Event Loop Cycle - The application processes events continuously
  2. Resource Management - Handles memory, file descriptors, and timers
  3. Error Handling - Catches and processes unhandled errors

Shutdown Phase

  1. Signal Reception - Receives termination signals (SIGINT, SIGTERM)
  2. Graceful Close - Completes pending work and closes connections
  3. Cleanup - Releases resources and exits
Receive SignalComplete Pending WorkClose ResourcesRelease ResourcesrunSignal ReceivedStart ShutdownContinue ClosingFinal Cleanup
Node.js Process Shutdown Flow

The shutdown diagram now uses valid diagram syntax with only boxes, arrows, and a caption.

Real-World Process Behavior

Here's a practical example showing how the event loop handles different types of operations:

process-lifecycle.tsTypeScript
import { Worker } from 'worker_threads';import { parentPort } from 'worker_threads'; // CPU-intensive work in a worker threadparentPort.on('message', (data) => {  const result = fibonacci(data.n);  parentPort.postMessage(result);}); function fibonacci(n: number): number {  if (n <= 1) return n;  return fibonacci(n - 1) + fibonacci(n - 2);}

In this example:

  1. The main thread receives a request
  2. For CPU-intensive work like fibonacci, it delegates to a worker thread
  3. The event loop remains free to handle other I/O operations
  4. When the worker completes, it sends the result back

Key Takeaways

  • Node.js uses a single-threaded event loop with auxiliary threads for specific tasks
  • The event loop runs in six phases: Timers → Pending → Idle → Poll → Check → Close
  • Worker threads allow CPU-intensive work without blocking the main thread
  • Understanding the lifecycle helps with debugging and performance optimization
  • Graceful shutdown is crucial for production applications

The design of Node.js prioritizes non-blocking I/O operations, making it exceptionally well-suited for applications that need to handle many concurrent connections, such as web servers, APIs, and real-time communication platforms.