Why the code after the call printed first
In lesson 1-2, loadConfig(callback) used setTimeout before calling the callback, and the line after the loadConfig(...) call printed first because setTimeout schedules the callback for later, and Node keeps running the rest of the file instead of waiting.
Node is non-blocking, so setTimeout hands the callback to a timer and execution continues immediately. Only after the current code finishes and the delay passes does the callback run.
The useful way to say it is that loadConfig returns before its work is done. It returned undefined a fraction of a millisecond after being called, having arranged for something to happen rather than having made it happen.
Hold onto this model, because lesson 2-2 draws the machinery behind it.
Why modules exist
A real backend is thousands of lines. Nobody sane keeps that in one file. Node lets you split code into modules: files that export some values and import what they need from others.
The classic Node style is called CommonJS. A file exports by assigning to module.exports, and imports with require:
// mathUtils.js function add(a, b) { return a + b; } module.exports = { add };
// app.js const mathUtils = require("./mathUtils"); console.log(mathUtils.add(2, 3)); // 5
The ./ means "a file next to me". Without it, require("fs") reaches for a built-in module, or one installed with npm, Node's package manager: a command-line tool plus a public registry of shared code, which you will use to install Express in unit 5.
The newer syntax: ES modules
Modern JavaScript added its own module syntax, ESM, which Node also supports (in .mjs files or with "type": "module" in package.json):
// mathUtils.mjs export function add(a, b) { return a + b; }
// app.mjs import { add } from "./mathUtils.mjs"; console.log(add(2, 3));
| CommonJS | ESM | |
|---|---|---|
| export | module.exports = {...} | export |
| import | require(...) | import ... from |
| where you meet it | most Node tutorials and older code | new projects, frontend code |
Either way, the shape is the same: a module is an object of things it chose to share. The exercises here run in one file, so you will practice that shape directly.
A module as an object of shared functions
The mathUtils object plays the role of module.exports, bundling related functions while everything else stays private.
// Imagine this object is what mathUtils.js exports const mathUtils = { add(a, b) { return a + b; }, average(list) { let sum = 0; for (const n of list) sum += n; return sum / list.length; }, }; console.log(mathUtils.add(2, 3)); console.log(mathUtils.average([10, 20, 30]));
Output
5 20
average sums 10 plus 20 plus 30 to get 60 and divides by the 3 items. Both functions are reached through the object, which is exactly how a required module is used.
The privacy point is the one worth taking away. Anything a file declares and does not put on the exported object is invisible to importers, so a module has a public surface and an interior, and shrinking that surface is most of what makes code maintainable.
The shorthand method syntax here, add(a, b) { ... }, is the same as writing add: function (a, b) { ... }. It is from Advanced JavaScript and is the usual way module objects are written.
Note that average([]) returns NaN, since the sum is 0 and the length is 0. Real module code would guard that case, and finding it is a good habit to practice on every function you export.
A format module
One function, money(cents), turns 1234 into the string "$12.34" by dividing by 100 and fixing two decimals.
const format = { money(cents) { return "$" + (cents / 100).toFixed(2); }, }; console.log(format.money(1234)); console.log(format.money(50));
Output
$12.34 $0.50
Dividing 1234 by 100 gives 12.34, and toFixed(2) on 0.5 gives the string "0.50" rather than "0.5", which is why the second line has the trailing zero. Concatenating the dollar sign in front finishes the job.
toFixed returning a string is the property being used here, and it is the reason this works at all. Plain arithmetic cannot represent "0.50 with a trailing zero", since that is a formatting fact rather than a numeric one.
Storing money as integer cents is the real lesson hiding in the example. Floating point cannot represent 0.1 exactly, so adding prices as decimals accumulates error, and keeping cents as whole numbers avoids the problem entirely until display time.
A format module is a typical thing to extract, because the same currency rendering is needed by receipts, emails, and API responses. Putting it in one file means changing the currency symbol or the decimal count in one place.