Skip to content

11 — Control flow and iteration

One line: Every way of walking a sequence in JavaScript — for...of, spread, destructuring, generators, iterator helpers and for await — runs on one small protocol, [Symbol.iterator]() and next(), while the older statements keep rules of their own: switch compares with === and && returns an operand.

The chapter starts with the statements every program uses — if, switch, the conditional operator and the loops — and with &&, || and ??, which decide as much control flow as any statement. It then separates for...in, which walks keys, from for...of, which walks values, and opens up what for...of runs on: the iteration protocol. Generators write that protocol for you, iterator helpers chain lazily on top of it, and async iteration stretches it over values that arrive later.

The pages

Page Level In one line Status
if, switch and ? : 101 switch matches its cases with ===, so "1" never reaches case 1 and NaN never reaches case NaN — and a case without break falls through into the next one. stub
Loops 101 do...while always runs its body once, a plain break leaves only the innermost loop, and break outer leaves every loop out to the label — a label even lets break leave a plain block, though continue must name a loop. stub
Short-circuit evaluation 101 a || b, a && b and a ?? b hand back one of their operands, not true or false — 0 ?? 5 is 0 and "a" && "b" is "b" — and the right side never runs once the left side decides. stub
for...in and for...of 101 for...in walks an object's enumerable string keys, inherited ones included — on an array it yields "0", "1" and any extra property — while for...of walks the values an iterable hands out, and throws TypeError on a plain object. stub
The iteration protocol 201 Any object whose [Symbol.iterator]() returns something with a next() method works with for...of, spread and destructuring — and when a break or a short destructuring stops early, JavaScript calls the iterator's return() so it can clean up. stub
Generators 201 Calling a generator function runs none of its body: each next() runs it to the following yield, the argument of next(x) becomes that yield's value, and the generator object can be walked only once. stub
Iterator helpers 201 map, filter, take and the other methods on Iterator.prototype do nothing until a value is asked for, so naturals().map(square).take(3) works on an endless generator and calls square exactly three times. stub
Async iteration 301 for await...of waits for each value before asking for the next, so looping over an array of running promises is a trap: one that rejects before the loop reaches it is unhandled, and Node exits with status 1 despite the try around the loop. stub

Boundaries

Closures made inside loops belong to Variables and scope, the array methods map, filter and reduce to Arrays and collections, and TypeScript's exhaustive switch to Narrowing.