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:
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 thread | Offloaded to libuv’s thread pool |
| V8 waits — cannot move to next line | V8 immediately moves to next line |
| libuv is NOT involved | Callback runs when file read is done |
| Event loop is frozen | Event loop stays responsive |
| Application becomes unresponsive | Other 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:
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);Hello World
Multiplication result is : 22637556228
Second Key is generatedNotice 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:
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);Hello World
First Key is Generated
Multiplication result is : 22637556228
Second Key is generatedThe 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?
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);Hello World
Multiplication result is : 22637556228
call me right now
call me after 3 secondsWait — "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:
| Step | What executes | Where |
|---|---|---|
| 1 | console.log("Hello World") | V8 call stack (sync) |
| 2 | a = 1078698, b = 20986 | V8 call stack (sync) |
| 3 | setTimeout(..., 0) — offloaded to libuv | libuv timer queue |
| 4 | setTimeout(..., 3000) — offloaded to libuv | libuv timer queue |
| 5 | multiplyFn(a, b) — sync execution | V8 call stack (sync) |
| 6 | console.log("Multiplication result...") | V8 call stack (sync) |
| 7 | Call stack empty → event loop kicks in | Event loop |
| 8 | "call me right now" — timer(0) callback runs | V8 via event loop |
| 9 | "call me after 3 seconds" — timer(3000) callback | V8 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:
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);Hello World
========
First Key is Generated
Multiplication result is : 22637556228
call me right now !!!!
Second Key is generatedEpisode 07 — at a glance
| Concept | Key detail |
|---|---|
| V8 speed | Executes synchronous code in microseconds — variable assignment, math, function calls are instant |
| Sync functions | Any function ending in Sync blocks the main thread. V8 waits, event loop freezes. Examples: readFileSync, pbkdf2Sync, writeFileSync |
| Async functions | Without the Sync suffix — offloaded to libuv (thread pool for file/crypto, OS kernel for network). V8 moves on immediately. |
| crypto module | Core Node.js module for cryptographic operations. Import with require("crypto") or require("node:crypto") |
| pbkdf2Sync | Synchronous key derivation — blocks the main thread for the entire computation. Never use in production servers. |
| pbkdf2 | Async 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 order | All sync code → then event loop → then timer callbacks → then I/O callbacks |
| Best practice | Always prefer async methods in production. Sync methods are acceptable only during app startup or in CLI scripts. |
| Interview tip | setTimeout(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, andPUBLIC_GISCUS_CATEGORY_IDto enable.