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.
| Concept | What it means | Decision rule |
|---|---|---|
| Event loop | Scheduling of callbacks around completed work | Keep each callback short and bounded |
| Microtask | Promise reaction processed before returning to phases | Avoid unbounded chains that starve I/O |
| Worker pool | libuv threads used by selected native APIs | Treat its size and queue as finite resources |
Professional workflow
Build and verify Node.js programs from the terminal in small, observable steps.
- Define the event-loop workload boundary: inputs, outputs, invariants, ownership, and expected failures.
- Design the data or message contract before choosing implementation details.
- Implement the smallest correct path with dependencies passed explicitly.
- Add validation, failure translation, cleanup, and concurrency behavior.
- Verify the boundary with realistic data and at least one adversarial case.
- Measure or observe the behavior before optimizing or extracting abstractions.
Keep the feedback loop short
Guided code lab
Measure responsiveness
Event-loop delay and event-loop utilization reveal saturation that request averages can hide.
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
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
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