Search lands in PR-5.1 (Pagefind).

Explanation Intermediate

Chapter 7 Updated

Sync, Async & the magic of setTimeout(0)

Sync methods freeze the event loop, crypto can block for seconds, and setTimeout(0) still waits for the call stack.

  • Full 20m
  • Revision 5m
  • Flow 2m

V8 is fast — but it can’t do everything alone

From Episode 06, we learned that V8 executes synchronous code in microseconds. Simple variable assignments, math, function calls — all happen almost instantly on the call stack. But V8 cannot handle async operations (file I/O, networking, timers) by itself. It offloads those to libuv, which talks to the OS and returns the callback to V8 when done.

This episode dives deeper into what happens when you use synchronous versions of normally-async operations, and introduces a tricky interview concept: setTimeout(0).

Blocking the main thread — fs.readFileSync()

The PDF shows a critical code example. Look at this program with both sync and async operations together:

xyz.js — mixing sync and async
console.log("Hello world");
 
var a = 107;
var b = 20;
 
// Synchronous — BLOCKS the main thread!
fs.readFileSync("./file.txt", "utf8"); // ~10ms
console.log("this will execute only after file read");
 
Https.get("https://dummyjson.com/products/1", (res) => {
  console.log("fetched data success");
});
 
setTimeout(() => {
  console.log("setTimeout called after 5 seconds");
}, 5000);
 
// Async function
fs.readFile("./file.txt", "utf8", (err, data) => {
  console.log("File Data : ", data);
});
 
function multiplyFn(x, y) {
  const result = a * b;
  return result;
}
Synchronous (blocking)Asynchronous (non-blocking)
fs.readFileSync()fs.readFile()
Blocks the main threadOffloaded to libuv’s thread pool
V8 waits — cannot move to next lineV8 immediately moves to next line
libuv is NOT involvedCallback runs when file read is done
Event loop is frozenEvent loop stays responsive
Application becomes unresponsiveOther requests can be handled

The crypto module — another blocking example

Node.js has a core module called crypto used for cryptographic operations like generating secure keys, hashing passwords, and more. It’s built into Node.js just like https, fs, and zlib.

The PDF shows two code examples — one with pbkdf2Sync (blocking) and one with pbkdf2 (async). Here’s the first example:

blocking.js — crypto.pbkdf2 async only
const crypto = require("node:crypto");
 
console.log("Hello World");
 
var a = 1078698;
var b = 20986;
 
// pbkdf2 - Password Base Key Derivative Function
 
// Async Function — offloaded to libuv
crypto.pbkdf2("password", "salt", 5000000, 50, "sha512", (err, key) => {
  console.log("Second Key is generated");
});
 
function multiplyFn(x, y) {
  const result = a * b;
  return result;
}
 
var c = multiplyFn(a, b);
 
console.log("Multiplication result is : ", c);
Output
Hello World
Multiplication result is :  22637556228
Second Key is generated

Notice the output order: “Hello World” and the multiplication result print first (synchronous), then “Second Key is generated” prints last (async — offloaded to libuv’s thread pool). Now here’s the version with both sync and async:

blocking.js — sync + async crypto
const crypto = require("node:crypto");
 
console.log("Hello World");
 
var a = 1078698;
var b = 20986;
 
// Synchronous — BLOCKS THE MAIN THREAD — DON'T USE IT
crypto.pbkdf2Sync("password", "salt", 50000000, 50, "sha512");
console.log("First Key is Generated");
 
// Async Function — offloaded to libuv
crypto.pbkdf2("password", "salt", 5000000, 50, "sha512", (err, key) => {
  console.log("Second Key is generated");
});
 
function multiplyFn(x, y) {
  const result = a * b;
  return result;
}
 
var c = multiplyFn(a, b);
console.log("Multiplication result is : ", c);
Output
Hello World
First Key is Generated
Multiplication result is :  22637556228
Second Key is generated

The magic of setTimeout(0) — a classic interview question

This is one of the most asked JavaScript/Node.js interview questions. What happens when you set a timer with 0 milliseconds delay?

setTimeoutZero.js
console.log("Hello World");
 
var a = 1078698;
var b = 20986;
 
setTimeout(() => {
  console.log("call me right now ");
}, 0); // Trust issues with setTimeout!
 
setTimeout(() => {
  console.log("call me after 3 seconds");
}, 3000);
 
function multiplyFn(x, y) {
  const result = a * b;
  return result;
}
 
var c = multiplyFn(a, b);
 
console.log("Multiplication result is : ", c);
Output
Hello World
Multiplication result is :  22637556228
call me right now
call me after 3 seconds

Wait — "call me right now" has a delay of 0ms, but it prints AFTER the multiplication result? Shouldn’t 0ms mean “run immediately”?

Let me trace through the execution step by step:

StepWhat executesWhere
1console.log("Hello World")V8 call stack (sync)
2a = 1078698, b = 20986V8 call stack (sync)
3setTimeout(..., 0) — offloaded to libuvlibuv timer queue
4setTimeout(..., 3000) — offloaded to libuvlibuv timer queue
5multiplyFn(a, b) — sync executionV8 call stack (sync)
6console.log("Multiplication result...")V8 call stack (sync)
7Call stack empty → event loop kicks inEvent loop
8"call me right now" — timer(0) callback runsV8 via event loop
9"call me after 3 seconds" — timer(3000) callbackV8 via event loop

The complete picture — all three examples combined

The PDF’s last code screenshot shows the most complex example combining everything — sync crypto, setTimeout(0), async crypto, and synchronous math:

blocking.js — everything combined
const crypto = require("node:crypto");
 
console.log("Hello World");
 
var a = 1078698;
var b = 20986;
 
// pbkdf2 - Password Base Key Derivative Function
// Synchronous — BLOCKS THE MAIN THREAD
console.log("========");
crypto.pbkdf2Sync("password", "salt", 50000000, 50, "sha512");
console.log("First Key is Generated");
 
setTimeout(() => {
  console.log("call me right now !!!! ");
}, 0); // it will only be called once call stack of main thread is empty
 
// Async Function
crypto.pbkdf2("password", "salt", 5000000, 50, "sha512", (err, key) => {
  console.log("Second Key is generated");
});
 
function multiplyFn(x, y) {
  const result = a * b;
  return result;
}
 
var c = multiplyFn(a, b);
console.log("Multiplication result is : ", c);
Output
Hello World
========
First Key is Generated
Multiplication result is :  22637556228
call me right now !!!!
Second Key is generated

Episode 07 — at a glance

ConceptKey detail
V8 speedExecutes synchronous code in microseconds — variable assignment, math, function calls are instant
Sync functionsAny function ending in Sync blocks the main thread. V8 waits, event loop freezes. Examples: readFileSync, pbkdf2Sync, writeFileSync
Async functionsWithout the Sync suffix — offloaded to libuv (thread pool for file/crypto, OS kernel for network). V8 moves on immediately.
crypto moduleCore Node.js module for cryptographic operations. Import with require("crypto") or require("node:crypto")
pbkdf2SyncSynchronous key derivation — blocks the main thread for the entire computation. Never use in production servers.
pbkdf2Async key derivation — offloaded to libuv thread pool. Event loop stays responsive.
setTimeout(0)Does NOT run at 0ms. The callback is async — it’s offloaded to libuv and only runs after the call stack is empty. The 0 is a minimum threshold, not a guarantee.
Execution orderAll sync code → then event loop → then timer callbacks → then I/O callbacks
Best practiceAlways prefer async methods in production. Sync methods are acceptable only during app startup or in CLI scripts.
Interview tipsetTimeout(0) output questions test understanding of the call stack, event loop, and callback queues. Sync always runs before any async callback.

Comments

Comments are disabled in this environment. Set PUBLIC_GISCUS_REPO, PUBLIC_GISCUS_REPO_ID, and PUBLIC_GISCUS_CATEGORY_ID to enable.