01. How Node Runs Async
Node.js runs your JavaScript on a single main thread driven by an event loop. That one thread is precious — block it and every in-flight request stalls.
How the loop works
- Each turn drains the microtask queue first (resolved
Promisecallbacks,process.nextTick), then a batch of macrotasks (completed I/O callbacks, timers). - Asynchronous I/O (file reads, network, a database round-trip) is handed to libuv, which uses the OS's native async facilities where possible and a small thread pool otherwise. The main thread stays free.
The rule interviewers probe
Single-threaded does not mean no concurrency. The loop juggles thousands of concurrent I/O-bound operations; what it can't do is two CPU-bound computations at once.
// ❌ blocks the loop — every other request waits on this hash
const hash = crypto.pbkdf2Sync(pw, salt, 1_000_000, 64, "sha512");
// ✅ stays async — libuv runs it on the thread pool
const hash = await promisify(crypto.pbkdf2)(pw, salt, 1_000_000, 64, "sha512");
For genuinely CPU-heavy work (large data transforms, local model inference) move it off the loop with worker threads. Node shines for many small, I/O-heavy concurrent operations; it is the wrong tool for long synchronous computation on the request path.
Ask yourself: why is a single blocked turn so dangerous on a Node service? Because that one main thread is precious — block it and every in-flight request stalls behind it.
Generate it: each loop turn drains the m________ queue (resolved Promise callbacks, process.nextTick) first, then a batch of macrotasks. → microtask
Order it like a two-step routine: every turn the loop drains the microtask queue first (resolved promises, process.nextTick), then runs a batch of macrotasks (completed I/O callbacks, timers). Micro before macro, every turn.
Generate it: work the loop can't do — two C__-bound computations at the same time. → CPU-bound (it can juggle thousands of concurrent I/O-bound operations, just not parallel CPU work).
Tie it together: async I/O — file reads, network, a database round-trip — is handed to libuv, which uses the OS's native async facilities where possible and a small thread pool otherwise, so the main thread stays free.
Ask yourself: when the work is genuinely CPU-heavy (large data transforms, local model inference), where does it go? Off the loop, onto worker threads — Node shines for many small, I/O-heavy concurrent operations, not long synchronous computation on the request path.
Recall check (try before reading the answer):
- In one loop turn, which queue is drained first — microtasks or macrotasks? Answer: Microtasks — resolved promises and
process.nextTick— before the macrotask batch.- Does single-threaded mean no concurrency? Answer: No — the loop juggles thousands of concurrent I/O-bound operations; it just can't run two CPU-bound computations at once.
- Where should CPU-heavy work go? Answer: Off the loop, onto worker threads.
Sync PBKDF2 blocks the event loop; async PBKDF2 runs on libuv's thread pool.
// ❌ blocks the loop — every other request waits on this hash
const hash = crypto.pbkdf2Sync(pw, salt, 1_000_000, 64, "sha512");
// ✅ stays async — libuv runs it on the thread pool
const hash = await promisify(crypto.pbkdf2)(pw, salt, 1_000_000, 64, "sha512");
Each loop turn drains the ____ queue first, then a batch of ____ .
Show answer
microtask, macrotasks
From the research: Flashcards / Anki cloze-deletion spaced repetition — The Effect of Spaced Repetition Learning Through Anki on Medical Board Exam Performance (2023)
Review "How Node Runs Async" briefly before bed — sleep helps your brain consolidate the event-loop model.
From the research: Sleep-dependent memory consolidation — An update on recent advances in targeted memory reactivation during sleep (2024)
Rate your confidence (1-5) in explaining the key idea of this chapter — how Node's single-threaded event loop handles async I/O, microtasks vs macrotasks, and when to offload CPU work — from memory, without looking back. Warning: rereading feels easy but doesn't build durable memory; self-test instead.
From the research: Desirable difficulties & metacognitive calibration — Learning concepts and categories: Is spacing the enemy of induction? (2008)
Explain why Node's single-threaded event loop can handle thousands of concurrent I/O operations but cannot execute two CPU-bound computations simultaneously. What is the fundamental difference that enables this concurrency?
From the research: Elaborative interrogation / self-explanation — Generation and Precision of Elaboration: Effects on Intentional and Incidental Learning (1987)
Alternate practice between how Node runs async and database concurrency control. Ask yourself: What differs between managing concurrency on the Node event loop vs in a database transaction?
Pair with: Database Concurrency Control
From the research: Interleaving / discriminative contrast — Similarity matters: A meta-analysis of interleaved learning and its moderators (2019)
In one loop turn, which queue is drained first — microtasks or macrotasks?
Show answer
Microtasks — resolved promises and process.nextTick — before the macrotask batch.
From the research: Retrieval practice / the testing effect — The Critical Importance of Retrieval for Learning (2008)
Review "How Node Runs Async" tomorrow, then again in 3 days, then in a week.
From the research: Spacing / distributed practice & spaced-repetition scheduling — Using Spacing to Enhance Diverse Forms of Learning: Review of Recent Research and Implications for Instruction (2012)