Checkpoint: Node Core

A checkpoint is a hands-on review that combines the Node core skills you've built so far — modules, the process object, crypto, and promises — into one real program you write and run yourself.

Learn Checkpoint: Node Core in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this checkpoint you'll recap everything from modules and buffers through timers, child processes, workers, crypto, and promises, then prove it by building a small command-line tool and answering a short quiz.

What This Checkpoint Covers

📚 What You've Learned

You've covered a lot of ground across the Node core track. Here's the map of where you are before you put it all together:

🛠️ Build Challenge: a Parallel Hash CLI

Put three skills together in one tool. Your program reads words from the command line ( process.argv ), hashes each one with SHA-256 ( crypto ), runs all the hashes together ( Promise.all ), and prints a short digest per word. Start from the scaffold below and fill in the four TODO s.

Try the challenge first. When you're ready to compare, expand the solution below — it's the complete, runnable program with its real output.

📝 Checkpoint Quiz

Answer each before expanding it. If any feel uncertain, the FAQ below points you to the lesson to review.

Index 2 . Index 0 is the path to node and index 1 is the script path, so your arguments start at argv[2] — hence process.argv.slice(2) .

Plain hashes are too fast , so attackers can guess billions per second. scrypt/pbkdf2 are deliberately slow key-derivation functions, and combined with a unique per-user salt they make brute-forcing impractical.

all rejects as soon as any input rejects and otherwise resolves with all values. allSettled never rejects — it waits for every promise and reports each as fulfilled or rejected.

process.nextTick . Its queue drains before the event loop reaches the timers phase, so it always runs before a zero-delay setTimeout .

For CPU-bound JavaScript you want to keep in the same process with fast message passing — heavy computation that would block the event loop. Use child_process to run external programs or for full process isolation.

A normal compare can stop at the first differing byte, leaking through timing how much matched. timingSafeEqual runs in constant time, closing that side channel.

Practice quiz

In process.argv, which index holds the first argument YOU typed on the command line?

  • 0
  • 1
  • 2
  • 3

Answer: 2. Index 0 is the path to node and index 1 is the script path, so your arguments start at argv[2] — hence process.argv.slice(2).

Why must passwords use scrypt or pbkdf2 instead of a plain SHA-256 hash?

  • Plain hashes are too fast, so attackers can guess billions per second
  • SHA-256 is not available in Node
  • SHA-256 cannot produce a hex digest
  • scrypt is the only function that adds a salt

Answer: Plain hashes are too fast, so attackers can guess billions per second. scrypt/pbkdf2 are deliberately slow key-derivation functions; combined with a unique per-user salt they make brute-forcing impractical.

What is the key difference between Promise.all and Promise.allSettled?

  • all runs sequentially while allSettled runs in parallel
  • allSettled resolves faster than all
  • There is no difference
  • all rejects as soon as any input rejects; allSettled never rejects and reports every result

Answer: all rejects as soon as any input rejects; allSettled never rejects and reports every result. Promise.all short-circuits on the first rejection; Promise.allSettled waits for every promise and labels each as fulfilled or rejected.

Which runs first: a process.nextTick callback or a setTimeout(fn, 0)?

  • setTimeout(fn, 0)
  • process.nextTick
  • They run at the same time
  • Whichever was scheduled first

Answer: process.nextTick. The nextTick queue drains before the event loop reaches the timers phase, so it always runs before a zero-delay setTimeout.

When would you choose worker_threads over child_process?

  • For CPU-bound JavaScript you want in the same process with fast message passing
  • To run an external program like git or ffmpeg
  • When you need full operating-system process isolation
  • For any I/O-bound task

Answer: For CPU-bound JavaScript you want in the same process with fast message passing. worker_threads keep heavy JS computation in one process with shared-memory message passing; child_process is for running external programs or full isolation.

Why compare secret values with crypto.timingSafeEqual instead of ===?

  • === cannot compare Buffers
  • timingSafeEqual is faster than ===
  • timingSafeEqual runs in constant time, closing a timing side channel
  • === always returns false for secrets

Answer: timingSafeEqual runs in constant time, closing a timing side channel. A normal compare can stop at the first differing byte, leaking via timing how much matched; timingSafeEqual takes constant time regardless.

In the hash CLI build challenge, how are all the words hashed together?

  • With a for-await loop, one at a time
  • With await Promise.all(words.map(hashWord))
  • With Promise.race over the words
  • Synchronously inside forEach

Answer: With await Promise.all(words.map(hashWord)). Promise.all(words.map(hashWord)) runs the hashes concurrently and returns an array of digests in the original word order.

How should a CLI report a missing-argument error and signal failure to the shell?

  • console.log the message and return
  • throw a string and let Node print it
  • console.log and call process.exit(0)
  • console.error the usage line and call process.exit(1)

Answer: console.error the usage line and call process.exit(1). Errors go to stderr via console.error so they are not mixed into piped data, and a non-zero exit code (1) tells shells and CI the command failed.

What import form does the hashcli use to access the built-in crypto module?

  • const crypto = require('crypto')
  • import crypto from 'node:crypto'
  • import { crypto } from 'crypto'
  • const crypto = await import('crypto')

Answer: import crypto from 'node:crypto'. The .mjs file uses ESM with the node: prefix: import crypto from 'node:crypto', the modern way to reference built-ins.

A common pitfall is reusing one Hash object for multiple words. What is the fix?

  • Call .reset() between words
  • Use a single global hash and ignore the error
  • Create a fresh crypto.createHash per word
  • Switch from sha256 to md5

Answer: Create a fresh crypto.createHash per word. A Hash object can only be digested once ('digest already called'), so each word needs its own fresh createHash('sha256').