Node.js MCQs multiple-choice questions with answers & explanations
All 29 Node.js quiz questions on one page. Pick an answer in your head, then open Show answer to check it and read why. Want a score and a timer? Take them as a quiz instead.
- 1.mid
In a CommonJS script run with
node, what is the output order?setTimeout(() => console.log('timeout'), 0); setImmediate(() => console.log('immediate')); process.nextTick(() => console.log('nextTick')); Promise.resolve().then(() => console.log('promise')); console.log('sync');- Async, nextTick, promise, timeout, immediate, always in that order
- Bsync, promise, nextTick, timeout, immediate
- CnextTick, sync, promise, immediate, timeout
- Dsync, nextTick, promise, then timeout and immediate in an order that is not guaranteed
Show answer
Answer: D (sync, nextTick, promise, then timeout and immediate in an order that is not guaranteed)
Synchronous code runs first, then Node drains the
process.nextTickqueue, then promise microtasks. In the main module, whether the timer (a 0 ms timeout becomes 1 ms) has already expired when the event loop first enters the timers phase depends on process performance, so the Node docs call the timeout/immediate order non-deterministic. Inside an I/O callback,setImmediatealways runs first. - 2.hard
What is the output order?
const fs = require('node:fs'); fs.readFile(__filename, () => { setTimeout(() => console.log('timeout'), 0); setImmediate(() => console.log('immediate')); process.nextTick(() => console.log('nextTick')); });- AnextTick, timeout, immediate
- BnextTick, immediate, timeout
- Cimmediate, nextTick, timeout
- DnextTick, then timeout and immediate in a non-deterministic order
Show answer
Answer: B (nextTick, immediate, timeout)
The
readFilecallback runs in the poll phase. When it returns, the nextTick queue is drained, and the loop then continues to the check phase, wheresetImmediatecallbacks run, before wrapping around to the timers phase on the next iteration. That's why the order is deterministic inside I/O callbacks. - 3.hard
In a CommonJS script, what is the output order?
Promise.resolve() .then(() => { console.log('p1'); process.nextTick(() => console.log('tick inside promise')); }) .then(() => console.log('p2')); process.nextTick(() => console.log('tick')); console.log('sync');- Async, tick, p1, tick inside promise, p2
- Bsync, tick, p1, p2, tick inside promise
- Csync, p1, p2, tick, tick inside promise
- Dsync, p1, tick, tick inside promise, p2
Show answer
Answer: B (sync, tick, p1, p2, tick inside promise)
After the main script, Node drains the nextTick queue (
tick), then the whole microtask queue:p1runs and schedules a new tick, and resolving its promise queuesp2, which runs before control returns to the nextTick queue. In an ES module the output differs (sync, p1, p2, tick, tick inside promise) because the module body itself runs inside a promise job, so microtasks drain first. - 4.easy
What is logged?
const { EventEmitter } = require('node:events'); const bus = new EventEmitter(); bus.on('order', (id) => console.log('listener', id)); console.log('before'); bus.emit('order', 42); console.log('after');- Abefore, listener 42, after
- Bbefore, after, listener 42
- Clistener 42, before, after
- Dbefore, after (the listener runs on a later tick and its output is lost)
Show answer
Answer: A (before, listener 42, after)
emit()calls every listener synchronously, in registration order, before it returns. EventEmitter isn't asynchronous by itself; if a listener needs to defer work it must usesetImmediate,process.nextTickor a promise. - 5.mid
What happens when this runs?
const { EventEmitter } = require('node:events'); const bus = new EventEmitter(); bus.emit('error', new Error('boom')); console.log('still running');- AIt logs 'still running'; the unhandled event is ignored
- BIt prints a warning, then logs 'still running'
- CThe error is queued until an error listener is added
- D
emitthrows the error, so the process crashes before 'still running'
Show answer
Answer: D (
emitthrows the error, so the process crashes before 'still running')The
'error'event is special: if nothing is listening,emit('error', err)throwserr. Any other event without listeners just returnsfalse. Always attach an'error'listener to emitters and streams you create. - 6.easy
What does
main.jslog?// greet.js exports = { hello: 'world' }; // main.js const greet = require('./greet'); console.log(greet);- A{ hello: 'world' }
- Bundefined
- C{}
- DTypeError: exports is read-only
Show answer
Answer: C ({})
exportsis just a local variable that starts out pointing atmodule.exports. Reassigning it breaks that link, andrequirereturnsmodule.exports, which is still the original empty object. Usemodule.exports = { ... }, or add properties withexports.hello = 'world'. - 7.easy
What does running
main.jsprint?// counter.js console.log('loading counter'); let count = 0; module.exports = { inc: () => ++count }; // main.js const a = require('./counter'); const b = require('./counter'); a.inc(); console.log(b.inc(), a === b);- Aloading counter, loading counter, 1 false
- Bloading counter, loading counter, 2 true
- Cloading counter, 1 false
- Dloading counter, 2 true
Show answer
Answer: D (loading counter, 2 true)
Modules are cached by resolved filename after the first
require, so the module body runs once and both calls return the same exports object. The sharedcountmakes a module behave like a singleton. ES modules are cached the same way. - 8.easy
What does this print?
console.log(Buffer.from('héllo').length, 'héllo'.length);- A5 5
- B6 5
- C5 6
- D10 5
Show answer
Answer: B (6 5)
A string's
lengthcounts UTF-16 code units, whileBuffer.fromencodes as UTF-8 by default, where 'é' takes two bytes. UseBuffer.byteLength(str)when you need a byte count, for example for aContent-Lengthheader. - 9.easy
With default settings in current Node versions, what happens when this script runs?
Promise.reject(new Error('nobody handles me')); setTimeout(() => console.log('still alive'), 100);- AA warning is printed, then 'still alive' is logged
- BNothing is printed except 'still alive'; the rejection is ignored
- CThe process crashes with the error and exit code 1; 'still alive' never prints
- DIt crashes only when
NODE_ENV=productionis set
Show answer
Answer: C (The process crashes with the error and exit code 1; 'still alive' never prints)
By default Node emits
'unhandledRejection', and if no handler is registered it raises the rejection as an uncaught exception, which terminates the process with exit code 1. (Older Node versions only printed a warning.) Handle rejections where they happen, and use a process-level handler only for logging. - 10.mid
What does this print?
let n = 0; setTimeout(() => console.log('timeout sees n =', n), 0); function spin() { if (++n < 100000) process.nextTick(spin); } spin();- Atimeout sees n = 100000
- Btimeout sees n = 1
- CA small number that varies between runs
- DNothing: the timeout never fires
Show answer
Answer: A (timeout sees n = 100000)
Node drains the entire nextTick queue, including ticks added while draining, before the event loop can move on, so recursive
nextTickstarves timers and I/O until the recursion stops. WithsetImmediate(spin)instead, each step waits for the next loop iteration, so the timer fires early with a small, varyingn. - 11.hard
What is the output order?
setImmediate(() => { console.log('immediate 1'); setImmediate(() => console.log('immediate 2')); process.nextTick(() => console.log('tick')); }); setImmediate(() => console.log('immediate 3'));- Aimmediate 1, immediate 3, tick, immediate 2
- Bimmediate 1, immediate 2, tick, immediate 3
- Cimmediate 1, tick, immediate 3, immediate 2
- Dimmediate 1, tick, immediate 2, immediate 3
Show answer
Answer: C (immediate 1, tick, immediate 3, immediate 2)
Node drains the nextTick and microtask queues after each individual
setImmediatecallback, sotickruns beforeimmediate 3. An immediate scheduled while the check phase is running is queued for the next loop iteration, soimmediate 2comes last. - 12.hard
What is the output order?
setTimeout(() => { console.log('t1'); Promise.resolve().then(() => console.log('p1')); process.nextTick(() => console.log('tick1')); }, 0); setTimeout(() => console.log('t2'), 0);- At1, t2, tick1, p1
- Bt1, tick1, p1, t2
- Ct1, p1, tick1, t2
- Dt1, t2, p1, tick1
Show answer
Answer: B (t1, tick1, p1, t2)
Node runs the nextTick queue and then the microtask queue after each timer callback (as browsers do), so both run before
t2. Within that,process.nextTickcallbacks have priority over promise reactions. - 13.mid
writable.write(chunk)returnsfalse. What should the code producing the data do?- ARe-send the chunk, because it was dropped
- BStop writing and resume when the stream emits
drain - CCall
end(), because the stream is full - DKeep writing:
falseonly means the disk is slow
Show answer
Answer: B (Stop writing and resume when the stream emits
drain)falseis backpressure: the chunk was accepted and buffered, but the internal buffer has reachedhighWaterMark. Writing more anyway makes memory grow without bound. Wait for'drain', or letpipe()/pipeline()handle this for you. - 14.mid
Which version satisfies the range
^0.2.3in package.json?- A0.3.0
- B0.9.0
- C1.0.0
- D0.2.9
Show answer
Answer: D (0.2.9)
A caret allows changes that don't modify the left-most non-zero part of the version. For
0.xversions the minor number is treated as breaking, so^0.2.3means>=0.2.3 <0.3.0. By contrast,^1.2.3allows everything below2.0.0, and~1.2.3allows only patch updates below1.3.0. - 15.easy
An HTTP handler calls
fs.readFileSync()on a large file for every request. What's the main problem under load?- A
readFileSyncis deprecated and slower thanreadFile - BIt exhausts the libuv thread pool, so other fs calls queue up
- CIt leaks a file descriptor on every call until the process restarts
- DIt blocks the event loop, so every other request waits while the file is read
Show answer
Answer: D (It blocks the event loop, so every other request waits while the file is read)
Synchronous fs APIs run on the main thread, so while the read is in progress Node can't run any other JavaScript or callbacks. Use
fs.promises.readFile, or better, stream it withfs.createReadStream(path).pipe(res)so memory stays flat too. Sync APIs are fine at startup, e.g. for reading config. - A
- 16.mid
A CPU-heavy image hash inside an Express route makes every request slow. What's the best fix within the same Node process?
- AWrap the hashing in
new Promise(...)so it runs asynchronously - BIncrease
UV_THREADPOOL_SIZE - CRun it in a pool of
worker_threads - DSchedule it with
process.nextTick
Show answer
Answer: C (Run it in a pool of
worker_threads)A Promise executor runs synchronously on the main thread, and
nextTickonly postpones the same blocking work. The libuv thread pool size affects Node's own async operations (fs,dns.lookup, crypto, zlib), not your JavaScript. Worker threads run JS on separate threads with their own event loops; reuse them through a pool (e.g. Piscina) rather than spawning one per request. - AWrap the hashing in
- 17.easy
What happens when you run
node app.mjs?// app.mjs console.log(__dirname);- AReferenceError: __dirname is not defined in ES module scope
- BIt prints the directory containing app.mjs
- CIt prints undefined
- DIt prints the current working directory
Show answer
Answer: A (ReferenceError: __dirname is not defined in ES module scope)
__dirname,__filename,require,moduleandexportsare injected by the CommonJS module wrapper and don't exist in ES modules. Useimport.meta.dirname/import.meta.filenamein recent Node versions, or derive them withfileURLToPath(import.meta.url). - 18.easy
What happens when you run
node script.cjs?// script.cjs const data = await Promise.resolve(42); console.log(data);- AIt prints 42
- BIt prints a pending Promise, because CommonJS ignores
await - CIt throws a SyntaxError: top-level
awaitis not allowed in CommonJS - DIt prints undefined, because the module finishes before the promise
Show answer
Answer: C (It throws a SyntaxError: top-level
awaitis not allowed in CommonJS)Top-level
awaitis only allowed in ES modules. Use a.mjsfile or"type": "module"in package.json, or wrap the code in an async function. - 19.hard
Both files are CommonJS. What does
node a.jsprint?// a.js exports.loaded = false; const b = require('./b'); exports.loaded = true; console.log('a sees b.aWasLoaded =', b.aWasLoaded); // b.js const a = require('./a'); exports.aWasLoaded = a.loaded;- Aa sees b.aWasLoaded = true
- Ba sees b.aWasLoaded = false
- Ca sees b.aWasLoaded = undefined
- DRangeError: Maximum call stack size exceeded
Show answer
Answer: B (a sees b.aWasLoaded = false)
When
b.jsrequiresa.jswhilea.jsis still executing, Node returns a's partially filledmodule.exportsfrom the cache instead of loading it again, sobseesloaded: false. Circular requires don't loop forever, but they expose incomplete exports; restructure the modules or read the property later, at call time. - 20.hard
On an 8-core machine with default settings, a single one of these hashes takes about T ms. Roughly when do the six callbacks fire?
const crypto = require('node:crypto'); const start = Date.now(); for (let i = 1; i <= 6; i++) { crypto.pbkdf2('secret', 'salt', 300000, 64, 'sha512', () => { console.log(i, Date.now() - start, 'ms'); }); }- AAll six at about T
- BOne after another, about T apart (T, 2T, ... 6T)
- CFour at about T, the other two at about 2T
- DAll six at about 6T
Show answer
Answer: C (Four at about T, the other two at about 2T)
Async
crypto.pbkdf2runs on libuv's thread pool, which has 4 threads by default, so four hashes run in parallel and the last two wait for a free thread. The main thread stays free throughout.UV_THREADPOOL_SIZE, set before the pool is first used, changes the size. - 21.mid
What happens when this runs?
const { EventEmitter } = require('node:events'); const e = new EventEmitter(); for (let i = 0; i < 11; i++) e.on('data', () => {}); e.emit('data'); console.log('done');- AAll 11 run and 'done' is logged, plus a MaxListenersExceededWarning
- BIt throws a RangeError when the 11th listener is added
- CThe 11th listener is silently dropped, so only 10 run
- DOnly the first 10 listeners run, and
emitreturns false
Show answer
Answer: A (All 11 run and 'done' is logged, plus a MaxListenersExceededWarning)
The default limit of 10 listeners per event is a leak detector, not a hard limit: Node only warns about a possible memory leak, typically caused by adding a listener per request and never removing it. Fix the leak, or raise the limit with
setMaxListeners()if many listeners are intentional. - 22.mid
You need to run the
convertCLI on a user-supplied filename. Which call avoids shell command injection?- A
exec('convert ' + file + ' out.png') - B
execFile('convert', [file, 'out.png']) - C
exec('convert "' + file + '" out.png') - D
spawn('convert ' + file, { shell: true })
Show answer
Answer: B (
execFile('convert', [file, 'out.png']))execandshell: truepass the whole string to a shell, so a filename likex; rm -rf ~becomes a second command, and adding quotes is easy to break out of.execFile(orspawnwithout a shell) passes arguments straight to the program. Still validate the input, e.g. a value starting with-could be read as an option. - A
- 23.mid
Why prefer
stream.pipeline(src, transform, dest, callback)oversrc.pipe(transform).pipe(dest)?- A
pipe()ignores backpressure, so fast sources overflow memory - B
pipeline()runs each stream in its own worker thread - C
pipe()only works with file streams, not sockets or transforms - D
pipeline()sends any stream's error to one callback and destroys all the streams
Show answer
Answer: D (
pipeline()sends any stream's error to one callback and destroys all the streams).pipe()does handle backpressure, but it doesn't propagate errors: if one stream fails, the others stay open (leaking file descriptors or sockets) unless you add error handlers to each.pipelinewires up errors and cleanup for the whole chain; a promise version is available fromnode:stream/promises. - A
- 24.mid
What's the vulnerability, and what's the fix?
const root = '/srv/app/public'; app.get('/files', (req, res) => { const filePath = path.join(root, req.query.name); res.sendFile(filePath); }); // GET /files?name=../../../etc/passwd- ANone:
path.joinstrips..segments from its arguments - BXSS: the filename must be HTML-escaped before joining
- CPath traversal: resolve the full path and reject it unless it stays inside
root - DCSRF: the request needs an anti-CSRF token
Show answer
Answer: C (Path traversal: resolve the full path and reject it unless it stays inside
root)path.joinnormalizes..segments, which walks up and out ofroot, here to/etc/passwd. Resolve the path withpath.resolve(root, name)and check that it starts withroot + path.sep, or better, map an allow-listed file id to a path instead of accepting paths from users. - ANone:
- 25.easy
In a CI pipeline, why use
npm ciinstead ofnpm install?- AIt installs exactly what the lockfile specifies, and fails if it is out of sync
- BIt is faster because it skips the lockfile and resolves versions fresh
- CIt upgrades every dependency to the newest version its range allows
- DIt installs only
devDependencies, which is all CI needs
Show answer
Answer: A (It installs exactly what the lockfile specifies, and fails if it is out of sync)
npm cigives reproducible installs: it deletes any existingnode_modules, installs from the lockfile, and errors out on a mismatch.npm installmay resolve newer versions within your semver ranges and update the lockfile. - 26.mid
You publish a React component library. How should its package.json declare
react?- AIn
dependencies, so it installs automatically for every consumer - BIn
devDependenciesonly, since React is only needed at build time - CIn
peerDependencies(anddevDependencies), so the app supplies React - DIn
bundleDependencies, so the library ships its own React copy
Show answer
Answer: C (In
peerDependencies(anddevDependencies), so the app supplies React)A regular dependency could leave the app with two copies of React, which breaks hooks and context. A peer dependency says "I need the host app's
react, within this range"; modern npm installs peers automatically and reports conflicts. - AIn
- 27.mid
What should a
process.on('uncaughtException', handler)handler do in a production server?- ALog the error and keep serving requests as normal
- BRetry the operation that threw
- CNothing else: registering it is enough to make the process safe
- DLog it, do synchronous cleanup, then exit and let a process manager restart the service
Show answer
Answer: D (Log it, do synchronous cleanup, then exit and let a process manager restart the service)
After an uncaught exception the process is in an unknown state: half-finished operations, leaked resources, broken invariants. The Node docs recommend using the event only for synchronous cleanup before exiting. Let a supervisor such as systemd, Kubernetes or PM2 restart the process.
- 28.mid
What is the difference between
Buffer.alloc(size)andBuffer.allocUnsafe(size)?- A
allocUnsafeis deprecated and will be removed - B
alloczero-fills the memory;allocUnsafedoesn't, so it may hold old data - C
alloccreates a fixed-size buffer andallocUnsafea growable one - D
allocUnsafeis not bounds-checked, so writes past the end corrupt memory
Show answer
Answer: B (
alloczero-fills the memory;allocUnsafedoesn't, so it may hold old data)allocUnsafereturns uninitialized memory (possibly from a shared internal pool), which can hold leftover data from earlier allocations, including secrets. Only use it when you'll immediately overwrite every byte; otherwise useBuffer.alloc. - A
- 29.mid
What does the
clustermodule give you?- AMultiple threads that share one V8 heap and event loop
- BSeveral worker processes sharing one server port, each with its own memory
- CAutomatic load balancing of requests across several machines
- DA shared in-memory cache that all workers read and write
Show answer
Answer: B (Several worker processes sharing one server port, each with its own memory)
clusterforks worker processes (withchild_process.fork) and distributes incoming connections among them so one server can use every CPU core. Workers don't share memory, so sessions and caches must live in an external store such as Redis. In containers, one process per container scaled horizontally is often preferred.