Event Loop Best Practices
Rules for keeping the Node.js event loop responsive under load - offload CPU, batch I/O, and measure lag before users feel it.
Search across all documentation pages
Rules for keeping the Node.js event loop responsive under load - offload CPU, batch I/O, and measure lag before users feel it.
worker_threads or a queue.*Sync APIs (readFileSync, bcrypt.hashSync) in HTTP handlers. Use promise or callback async variants.JSON.parse input size at the edge and application. Large bodies block parsing synchronously.Promise.all for independent I/O, not sequential await in loops. Add concurrency limits for large batches.queueMicrotask or setImmediate over setTimeout(fn, 0) for deferral. Match deferral type to ordering needs.process.nextTick to batch work. It starves I/O and timers.try/catch awaited code in request handlers. Unhandled rejections take down the process.for await on streams instead of loading entire payloads into memory. Streams cooperate with backpressure.setTimeout/setInterval as approximate, not real-time clocks. Use external schedulers for wall-clock cron.setInterval callbacks. Chain setTimeout after work completes.SIGTERM). Or call .unref() only when intentionally fire-and-forget.monitorEventLoopDelay. Alert before user-facing p99 doubles.eventLoopUtilization alongside CPU. High ELU with moderate CPU signals sync blocks.blocked-at in staging to attribute blocks to stack traces. Not always-on in production.No long synchronous JavaScript on the main thread during request handling. Everything else follows from that.
No. async only yields at await points. Sync code between awaits still blocks.
Depends on hardware - profile. Rule of thumb: cap bodies at 1-10 MB for typical APIs; stream beyond that.
cluster duplicates HTTP listeners across cores. worker_threads shares one server with CPU pools. Often use both patterns in different layers.
No - guards, pipes, and interceptors still run on the main thread unless you offload explicitly.
Only if they offload to libuv thread pool or return async. Sync native calls block like JavaScript loops.
zlib.gzipSync blocks - use promisify(gzip) or zlib/promises in request paths.
Improved, but explicit error handling and loop hygiene remain your responsibility.
Search for Sync, tight loops, JSON.parse(req.body), and new middleware without benchmarks.
JSON Schema compilation is usually at startup; per-request validation is sync but fast - still profile large payloads.
Deferring work until after the current I/O poll phase - batching multiple socket reads before aggregation.
Detecting Event-Loop Blockage for metrics and worker_threads for fixes.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 16, 2026