BugForge
All lessons
Node.js · 16/19 5 min

Don't block the event loop

Sync fs, heavy loops and big JSON stall every request. How to spot and fix them.

Node serves every request on one thread. While your code runs, nothing else does: no other request, no timer, no I/O callback.

Common blockers

Blocker Fix
fs.readFileSync in a handler await fs.promises.readFile (sync is fine at startup)
bcrypt.hashSync, crypto.pbkdf2Sync the async versions run on the thread pool
a loop over 10 million items paginate, stream, or move to a worker thread
JSON.parse of a huge payload limit body size (express.json({ limit: "1mb" }))
catastrophic regex on user input simpler patterns, cap input length
while (Date.now() < t) {} "sleep" await new Promise((r) => setTimeout(r, ms))

Async isn't parallel

for (const id of ids) {
  await fetchUser(id);              // one at a time: total = sum of latencies
}
await Promise.all(ids.map(fetchUser));   // concurrent: total ≈ slowest call

For hundreds of ids, limit concurrency (batches of 10, or a small pool) so you don't flood the database.

Spotting it

  • One slow endpoint makes every endpoint slow → something is blocking.
  • Timers fire late (setTimeout(fn, 100) runs after 2 s) → the loop was busy.
  • CPU at 100% on one core with low traffic → a hot loop.

Real CPU work

Use worker_threads (or a job queue and a separate process) for image processing, big reports and hashing in bulk. Keep the request handler short: accept, enqueue, respond 202.

Next in Node.js
REST API conventions tests expect