Node.js essentials: 50 cards on the event loop, modules and streams
Node.js fundamentals: the event loop, ESM vs CommonJS, npm, streams, Buffer, fs/promises, process, error handling, worker threads and a basic HTTP server.
Practice now
Study without an account
Scheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1What is Node.js?A JavaScript runtime built on the V8 engine and libuv, for running JavaScript outside the browser: servers, CLIs and build tools
- 2Is Node.js single-threaded?Your JavaScript runs on one main thread, but I/O is handed to the operating system and libuv’s thread pool, so waiting on files or the network doesn’t block it
- 3What is the event loop?The loop that takes ready callbacks (finished I/O, due timers, and so on) from queues and runs them one at a time on the main thread whenever the call stack is empty
- 4The phases of the Node.js event loop, in ordertimers → pending callbacks → poll (I/O) → check (
setImmediate) → close callbacksTheprocess.nextTickqueue and promise microtasks are drained after each callback, between phases too. - 5In what order does this CommonJS script print?js
console.log('A') setTimeout(() => console.log('B'), 0) Promise.resolve().then(() => console.log('C')) process.nextTick(() => console.log('D')) console.log('E')A E D C B: synchronous code first, then thenextTickqueue, then promise callbacks, then timersIn an ES module the top level already runs inside a promise job, soCprints beforeD. - 6
setImmediatevssetTimeout(fn, 0)setImmediateruns in the check phase right after I/O polling;setTimeout(fn, 0)waits for the timers phase (with a 1 ms minimum)Inside an I/O callbacksetImmediatealways fires first; from the main module their order isn’t guaranteed. - 7What blocks the event loop?Long synchronous work: heavy loops,
readFileSyncon big files, hugeJSON.parsecalls, sync crypto, catastrophic regexes. Every other request waits until it finishes - 8What runs on libuv’s thread pool?File system calls,
dns.lookup, CPU-heavy crypto likepbkdf2andscrypt, and zlib compression; it has 4 threads by default (UV_THREADPOOL_SIZE)Network sockets don’t use the pool; they rely on OS mechanisms such as epoll and kqueue. - 9Import and export in CommonJS vs ES modulesjs
// CommonJS const { readFile } = require('node:fs/promises') module.exports = { load } // ES modules import { readFile } from 'node:fs/promises' export { load } - 10How does Node decide whether a
.jsfile is CommonJS or ESM?ESM if the nearestpackage.jsonhas"type": "module", CommonJS otherwise;.mjsfiles are always ESM and.cjsfiles always CommonJSRecent versions also detectimport/exportsyntax in files with notypefield, but setting the field is clearer. - 11What replaces
__dirnamein an ES module?import.meta.dirname(andimport.meta.filename), available since Node 20.11; older code usesfileURLToPath(import.meta.url) - 12Why does this ESM import fail?js
import { sum } from './math'ES modules need the full file path, extension included:'./math.js'. There is no automatic.jsorindex.jsresolution as inrequire() - 13Can CommonJS
require()an ES module?Yes, since Node 22.12 and 20.19, as long as the module graph has no top-levelawait; on older versions useawait import() - 14Where can you use top-level
await?Only in ES modules, not in CommonJS files - 15Why import
node:fsinstead offs?Thenode:prefix always loads the built-in module, never an npm package with the same name; some newer built-ins likenode:testandnode:sqliterequire it - 16What do
^1.4.2and~1.4.2allow?^1.4.2allows>=1.4.2 <2.0.0(minor and patch updates);~1.4.2allows>=1.4.2 <1.5.0(patch updates only)Below 1.0 the caret is stricter:^0.4.2means<0.5.0. - 17
npm installvsnpm cinpm cideletesnode_modules, installs exactly what the lockfile says and fails if it disagrees withpackage.json; it never rewrites either file, which makes it right for CI - 18Should you commit
package-lock.json?Yes, for applications: it pins the whole dependency tree so every machine and deploy installs the same versions - 19
dependenciesvsdevDependenciesPackages needed at runtime vs tools only needed to build and test, like TypeScript or a test runner;npm install --omit=devskips the latter - 20What does
npxdo?Runs a package’s command-line tool: fromnode_modules/.binif it’s installed, otherwise it offers to download it temporarilybashnpx prettier --check . - 21The four kinds of Node.js streamsReadable, Writable, Duplex (both, like a TCP socket) and Transform (a duplex that changes data passing through, like gzip)
- 22Why use streams instead of reading a whole file?They process data chunk by chunk, so memory stays flat even for gigabyte files, and output can start before the input has finished
- 23What is backpressure?A slow writer telling a fast reader to wait:
write()returnsfalsewhen the buffer is full, and you should pause until the'drain'eventpipeline()and.pipe()handle backpressure for you. - 24Gzip a file with streamsjs
import { pipeline } from 'node:stream/promises' import { createReadStream, createWriteStream } from 'node:fs' import { createGzip } from 'node:zlib' await pipeline(createReadStream('app.log'), createGzip(), createWriteStream('app.log.gz')) - 25Why prefer
pipeline()over.pipe()?pipeline()forwards errors from any stage and destroys every stream on failure;.pipe()doesn’t propagate errors, which can leak file handles - 26Read a stream chunk by chunk without event handlersjs
for await (const chunk of stream) { process(chunk) }Readable streams are async iterables. - 27What is a
Buffer?A fixed-size chunk of raw binary data;Bufferis a subclass ofUint8Arraywith helpers for encodings like UTF-8, hex and base64 - 28What do these return?js
Buffer.byteLength('é') 'é'.length2and1: the string is 2 bytes in UTF-8 but 1 UTF-16 code unit - 29
Buffer.alloc(n)vsBuffer.allocUnsafe(n)allocfills the memory with zeros;allocUnsafeis faster but may contain old data, so overwrite it completely before use - 30Encode and decode base64js
Buffer.from('hi').toString('base64') // 'aGk=' Buffer.from('aGk=', 'base64').toString('utf8') // 'hi' - 31Read a text file with
fs/promisesjsimport { readFile } from 'node:fs/promises' const text = await readFile('notes.txt', 'utf8')Without an encoding you get aBuffer. - 32Why avoid
readFileSyncin a web server?It blocks the event loop, so every other request waits; it’s fine at startup or in short CLI scripts - 33Create a nested directoryjs
await mkdir('data/cache/images', { recursive: true })Withrecursive: trueit also succeeds if the directory already exists. - 34How should you check that a file exists before reading it?Don’t: just read it and handle the error, since the file can change between the check and the read. A missing file fails with
err.code === 'ENOENT' - 35What is
process.argvfor this command?bashnode app.js --port 3000[pathToNode, pathToApp, '--port', '3000']: your arguments start at index 2, so useprocess.argv.slice(2)util.parseArgsparses flags without a library. - 36What type are
process.envvalues?Always strings (orundefined):PORT=3000gives'3000', andDEBUG=falsegives the truthy string'false' - 37Load a
.envfile without the dotenv packagebashnode --env-file=.env app.jsAvailable since Node 20.6;process.loadEnvFile()does the same from code. - 38
process.exit(1)vsprocess.exitCode = 1process.exit()stops immediately, possibly before pending writes or async work finish; settingexitCodelets the process end naturally with that code - 39
process.cwd()vsimport.meta.dirnamecwd()is the directory the process was started from;dirnameis the folder of the current module file. Resolve files relative to the module - 40What is an error-first callback?The classic Node callback style: the first argument is an error (or
null) and the result comes secondjsfs.readFile(path, (err, data) => { if (err) return handle(err) use(data) }) - 41What happens on an unhandled promise rejection?Since Node 15 the process crashes with exit code 1 by default; always
awaitpromises or attach.catch() - 42What should a
process.on('uncaughtException')handler do?Log the error, then exit. The process may be in an inconsistent state, so resuming is unsafe; let a supervisor restart it - 43What happens when an EventEmitter emits
'error'with no listener?The error is thrown, which usually crashes the process; always handle'error'on streams, sockets and other emitters - 44Are EventEmitter listeners called asynchronously?No.
emit()calls every listener synchronously, in the order they were added, before it returns - 45What are worker threads for?Running CPU-heavy JavaScript in parallel; each worker has its own V8 instance and event loop and talks to the main thread with
postMessagejsimport { Worker } from 'node:worker_threads' const worker = new Worker(new URL('./hash.js', import.meta.url), { workerData: file }) worker.on('message', (digest) => console.log(digest)) - 46Do worker threads speed up I/O-heavy code?Not really: Node’s async I/O already avoids blocking. Workers help with CPU-bound tasks like image processing, parsing or hashing
- 47Write a minimal HTTP serverjs
import { createServer } from 'node:http' createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello') }).listen(3000)Forgetres.end()and the request hangs until it times out. - 48Read a JSON request body with plain
node:httpjsconst chunks = [] for await (const chunk of req) chunks.push(chunk) const body = JSON.parse(Buffer.concat(chunks).toString())reqis a Readable stream; the body arrives in chunks. - 49Does Node have
fetchbuilt in?Yes, globally since Node 18 (stable since 21), along withRequest,Response,HeadersandFormData; it’s built on undici - 50Can Node run a
.tsfile directly?Yes, since Node 22.18 and 23.6 it strips type annotations by default. It doesn’t type-check, and syntax that needs transforming, likeenum, requires--experimental-transform-types
Frequently asked questions
Which Node.js version does this deck target?
Current LTS releases. Cards note when a feature is recent, such as require() of ES modules (22.12), import.meta.dirname (20.11) and built-in TypeScript type stripping (22.18).
Do I need to know JavaScript first?
Yes. The deck assumes you know functions, promises and async/await; our JavaScript essentials deck covers those.
Does it cover Express or other frameworks?
No. It sticks to Node.js itself, which every framework builds on: the event loop, modules, streams and the built-in http module.
More in Web development
HTML & CSS
Fifty HTML and CSS cards: semantic HTML and accessible forms, specificity and cascade layers, flexbox and grid, stacking contexts and z-index, modern units and container queries.
50 cardsReact
Fifty React cards: state and re-rendering, useEffect and cleanup, memoization, refs, context, Suspense and the React 19 APIs like use, useOptimistic and useActionState.
50 cardsVue
Vue 3 fundamentals: ref vs reactive, computed and watch, props and emits, v-model, directives, slots, single-file components and Pinia stores.
50 cardsAngular
Modern Angular: standalone components, signals, inputs and outputs, dependency injection, RxJS basics, @if and @for, routing and OnPush change detection.
50 cardsNext.js
Next.js App Router basics: server vs client components, layouts, route handlers, server actions, caching, metadata, dynamic routes and middleware.
50 cardsGraphQL
GraphQL basics: schema types, queries, mutations, subscriptions, resolvers, variables, fragments, N+1 and DataLoader, introspection and GraphQL vs REST.
50 cards