Skip to the solution
MediumLow-Level Design7 min read

How does Node.js handle crypto operations efficiently without blocking the event loop?

967 views

The problem

Question presented to candidate: "You need to hash a password with a slow, deliberately expensive algorithm like scrypt. Would using the synchronous version inside a request handler cause a real, measurable problem β€” and how would you actually prove it either way?"

What a strong answer should cover:

  • Node's crypto module offers both synchronous (pbkdf2Sync, scryptSync) and asynchronous (pbkdf2, scrypt) variants for its computationally expensive functions β€” the sync versions run directly on the main thread, blocking it for the full duration; the async versions are dispatched to libuv's thread pool (covered fully in its own dedicated question), letting the main thread continue running other work while the computation happens elsewhere.
  • πŸ“Œ Verified, not just described: a 10ms heartbeat timer, run alongside both, showed pbkdf2Sync freezing it completely (0 ticks) for the full duration of the computation, while the async pbkdf2 doing the identical computation let the heartbeat tick 9 times during a comparable wall-clock duration β€” the exact same measurement technique used for blocking file I/O, applied here specifically to crypto.
  • This means the deliberate slowness that makes an algorithm like scrypt/pbkdf2 good for password hashing (covered in its own dedicated question) is not automatically a server-wide problem β€” using the async variant keeps that expensive computation from freezing every other concurrent request, because the actual work happens on a separate thread pool thread, not the main thread running the event loop.
  • The thread pool has a fixed, limited size (default 4, covered fully in its own dedicated question) β€” so async crypto is not free of contention either; enough concurrent expensive crypto calls will still queue behind that fixed pool, just without freezing the entire event loop the way the synchronous variant would.
  • A precise answer names which crypto operations actually go through the thread pool (the deliberately slow, CPU-intensive ones β€” pbkdf2/scrypt) versus lighter operations (hashing a small piece of data with createHash, HMAC) that are fast enough to run synchronously on the main thread with negligible blocking impact in practice, even though a synchronous API is used for them too.
  • The broader principle this demonstrates: "synchronous API" and "blocks the main thread" are the same fact stated twice for anything CPU-intensive β€” the fix is never a clever workaround, it is simply calling the asynchronous variant, which Node deliberately provides for exactly this reason.

Clarifying questions expected:

  • "Is this specifically about the deliberately slow password-hashing functions, or crypto operations generally?" β€” the blocking concern is really about the CPU-intensive ones specifically.
  • "Is the concurrency concern about one single slow operation, or many concurrent ones competing for the thread pool?" β€” decides whether async alone is sufficient or the thread pool's fixed size also needs consideration.

Code / implementation expected: Yes β€” the heartbeat-based measured comparison between pbkdf2Sync and pbkdf2 is the concrete, convincing proof, not a description of "async is non-blocking."

  • crypto
  • security
  • libuv

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 9 sections

Read the code

JavaScriptA 10ms heartbeat confirming pbkdf2Sync freezes the event loop while the async pbkdf2 doing identical work does not
const crypto = require("crypto");

let ticks = 0;
const hb = setInterval(() => ticks++, 10);

const t0 = Date.now();
crypto.pbkdf2Sync("pw", "salt", 300000, 64, "sha512"); // BLOCKING
console.log("pbkdf2Sync:", Date.now() - t0, "ms; ticks during it:", ticks);
// pbkdf2Sync: 141 ms; ticks during it: 0

const ticksBefore = ticks;
const t1 = Date.now();
crypto.pbkdf2("pw", "salt", 300000, 64, "sha512", () => { // NON-BLOCKING
  console.log("pbkdf2 (async):", Date.now() - t1, "ms; ticks during it:", ticks - ticksBefore);
  clearInterval(hb);
});
// pbkdf2 (async): 144 ms; ticks during it: 9

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 54 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

How does Node.js handle multi-threading behind the scenes if JavaScript is single-threaded?

Continue