A slow HTTP request does not prove that the Node.js event loop is blocked. Database waits, network queues, connection limits and downstream services can all add latency. Measure event loop delay alongside the request path so the evidence points to the responsible boundary.
JavaScript callbacks run on an event loop for an isolate, while Node also uses operating-system facilities, a worker pool for certain APIs, and optional worker threads. “Everything runs on one thread” is an unhelpful diagnosis. Synchronous JavaScript can still prevent that isolate from servicing other callbacks. Node’s event-loop guide explains the division of work.
Add a monitor with a clear lifetime
The following monitor samples delay and returns a cleanup function. It uses existing perf_hooks APIs and does not require a metrics service:
import { monitorEventLoopDelay } from "node:perf_hooks";
export function startLoopMonitor(report, intervalMs = 1000) {
if (!Number.isFinite(intervalMs) || intervalMs < 100) {
throw new RangeError("Choose an interval of at least 100 ms");
}
const histogram = monitorEventLoopDelay({ resolution: 20 });
histogram.enable();
const timer = setInterval(() => {
if (histogram.count > 0) {
report({
samples: histogram.count,
meanMs: histogram.mean / 1e6,
p99Ms: histogram.percentile(99) / 1e6,
maxMs: histogram.max / 1e6,
});
histogram.reset();
}
}, intervalMs);
timer.unref();
return () => {
clearInterval(timer);
histogram.disable();
};
}
The report callback should be short and should handle its own errors. In a service, call the returned function during shutdown. Do not construct another monitor on every request.
The interval-based histogram records nanoseconds; this example converts its reported values to milliseconds. Resolution controls sampling, so the numbers are not a direct measurement of how long every individual JavaScript task ran. Node’s perf_hooks documentation describes the API and histogram behavior.
Interpret the metric in context
Event-loop p99 is not HTTP p99. One describes sampled loop timing; the other describes completed requests. Keep the units and observation windows visible in the dashboard.
| Evidence | Investigation |
|---|---|
| Loop delay rises with a CPU-heavy route | Profile synchronous parsing, transformations and serialization |
| Requests slow down while loop delay is steady | Inspect database, network and queue timings |
| Average machine CPU looks low | Check per-core and per-process utilization before ruling out CPU work |
| Delay spikes during large payloads | Bound input size and inspect allocation and garbage-collection activity |
| Metrics change after a runtime upgrade | Compare sampling settings and instrumentation before attributing a regression |
These are diagnostic directions, not proofs. Compare repeated observations and controlled workloads rather than selecting an arbitrary “healthy under one millisecond” threshold.
Choose the remedy that fits the work
A synchronous CPU transformation may belong in a bounded worker pool. Creating a worker for every request adds another allocation and lifecycle problem; design queue capacity, cancellation and failure handling.
A large loop that can yield between independent items may be processed in chunks. Yielding lets other callbacks run, but it does not reduce the total CPU work. The chunk boundary must not expose half-applied state.
For expensive repeated calculations, a bounded cache can help if its keys, invalidation and memory budget match the workload. Hashing a large input and keeping every result forever are costs of their own.
For slow I/O, moving the waiting operation into a worker thread may solve nothing. Follow the trace to the constrained resource.
Prove the improvement
Use an isolated, repeatable workload with the same payload sizes and concurrency. Record response latency, errors, throughput, CPU, memory and loop delay before and after. Include shutdown and overload behavior.
Set alert thresholds from the service’s latency budget and observed baseline. This article supplies an instrumentation pattern and a diagnostic method; it does not claim production benchmark results or a universal safe delay.

Sardar Mukhshaf
Full-Stack & DevOps Engineer
I build scalable web applications, secure backend systems, and production-ready cloud infrastructure. My engineering focus spans interface design, API architecture, deployment pipelines, and observability. Follow for more deep dives into system architecture, performance engineering, and emerging technologies.
More from the practice
Keep the useful ideas close.
Follow new articles with your preferred RSS reader.
Open the RSS feed ↗


