Phase 5 · Advanced Node.js & ArchitectureModule 34~64 min read

Event Loop, libuv & Async Internals

Reason precisely about phases, microtasks, nextTick, the worker pool, async context, latency, and event-loop saturation.

What you'll learn

Node.js concurrency depends on a responsive JavaScript thread plus operating-system and libuv facilities. Distinguish event-loop phases, microtasks, worker-pool work, and CPU blocking so latency problems become diagnosable.

By the end of this lesson, you'll be able to:

  • Explain event-loop and microtask ordering
  • Identify worker-pool operations
  • Measure event-loop delay
  • Remove blocking work from request paths

Core mental model

Node.js becomes easier when you separate the JavaScript language from the runtime and the operating-system capabilities it exposes. Use this table as a decision guide.

ConceptWhat it meansDecision rule
Event loopScheduling of callbacks around completed workKeep each callback short and bounded
MicrotaskPromise reaction processed before returning to phasesAvoid unbounded chains that starve I/O
Worker poollibuv threads used by selected native APIsTreat its size and queue as finite resources

Professional workflow

Build and verify Node.js programs from the terminal in small, observable steps.

  1. Define the event-loop workload boundary: inputs, outputs, invariants, ownership, and expected failures.
  2. Design the data or message contract before choosing implementation details.
  3. Implement the smallest correct path with dependencies passed explicitly.
  4. Add validation, failure translation, cleanup, and concurrency behavior.
  5. Verify the boundary with realistic data and at least one adversarial case.
  6. Measure or observe the behavior before optimizing or extracting abstractions.

Keep the feedback loop short

Run the smallest useful command after every meaningful change. Read the complete error message before editing again, and keep inputs and outputs visible while you learn.

Guided code lab

Measure responsiveness

Event-loop delay and event-loop utilization reveal saturation that request averages can hide.

loop-health.js
import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
const delay = monitorEventLoopDelay({ resolution: 20 });
delay.enable();
let previous = performance.eventLoopUtilization();
setInterval(() => {
  const elu = performance.eventLoopUtilization(previous);
  previous = performance.eventLoopUtilization();
  metrics.gauge('event_loop_utilization', elu.utilization);
  metrics.gauge('event_loop_delay_p99_ms', delay.percentile(99) / 1e6);
  delay.reset();
}, 10_000).unref();

Production practice

Contract

Request work has explicit CPU, I/O, concurrency, timeout, and cancellation budgets.

Verification

Compare callback, Promise, nextTick, timer, and immediate ordering; then load-test with CPU and worker-pool saturation.

Operations

Alert on sustained event-loop delay and utilization, track worker-pool queues indirectly, and profile before tuning.

Common failure mode

Async syntax does not make CPU work asynchronous. A long loop inside an async function still blocks every connection.

Independent workshop

Diagnose and repair a latency spike caused by CPU work and microtask starvation.

Your finished workshop must include:

  • Ordering experiment
  • Event-loop metrics
  • Reproducible load test
  • CPU profile
  • Bounded-work fix
  • Before/after evidence

Definition of done

Run the happy path and at least two edge cases, keep responsibilities separated, and add a short README explaining how to run the program.

Recap & quick check

Key takeaways

  • One JS thread serves many requests
  • Callbacks must stay short
  • Microtasks can starve I/O
  • libuv resources are finite
  • Measure responsiveness directly

Quick check

1. Does async make CPU loops non-blocking?

2. What does event-loop delay reveal?

3. What can starve I/O?

Next: Worker Threads & Child Processes