Iterator.prototype.forEach can look like Array.prototype.forEach, but the source is one-shot and the method consumes it eagerly. That difference explains many “why is the iterator empty now?” moments.
The ECMAScript algorithm requires an object receiver and callable procedure, gets the direct iterator, calls the procedure with each value and a zero-based counter, returns undefined at completion, and closes the iterator when callback evaluation ends abruptly.
This guide teaches consumption traces, indices, callback errors, generator cleanup, infinite-source safety, side-effect discipline and the choice between forEach, map, reduce and for-of.
Punggol families can use this guide in short sessions around homework, CCAs and rest. The examples are proposed learning activities. They do not claim a physical tuition branch, timetable, fee, class size, school relationship or guaranteed result.
Use disposable data and repositories, preserve backups and check version-sensitive details against the linked primary source. Current documentation settles a technical contract; observation and explanation turn that contract into usable knowledge.
Find your next learning step
Choose the route that matches the present difficulty. A complete course can follow the index in order; a repair session can begin at the first unstable distinction.
Build the model
Chapters 1–4. Model an eager terminal consumer and one-shot source.
Read the contract
Chapters 5–8. Read callback, index, return and closing rules.
Test the boundary
Chapters 9–12. Test errors, short sources and infinite-source boundaries.
Diagnose and repair
Chapters 13–16. Diagnose shared consumption and side-effect failures.
Transfer with judgment
Chapters 17–20. Choose forEach, map, reduce or for-of by output needs.
Open the full chapter index · Jump to capstone practice · Use the How Studying Works hub · Read the primary technical source
Complete chapter index
Chapters 1–4 · Build the model
Chapters 5–8 · Read the contract
Chapters 9–12 · Test the boundary
Chapters 13–16 · Diagnose and repair
Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper. That is the invariant to preserve when names, data or surrounding project details change.
A terminal consumer ends the lazy-helper chain. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is calling forEach in a pipeline as though it were lazy. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
Iterator.from([1,2,3]).forEach(x=>console.log(x))Explained result. The three values are processed immediately and the call completes with undefined. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace call, each next step, callback and completion. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “forEach is eager and terminal”, separate contract from coincidence. The contract is “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Count pulls before, during and after the call.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “forEach is eager and terminal” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” should remain invariant and what observation would expose calling forEach in a pipeline as though it were lazy.
Reasoned route. Begin with “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” Apply this procedure: Count pulls before, during and after the call. For the homework result stream, introduce one controlled variation that exposes calling forEach in a pipeline as though it were lazy. The three values are processed immediately and the call completes with undefined. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “forEach is eager and terminal” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” should remain invariant and what observation would expose calling forEach in a pipeline as though it were lazy.
Reasoned route. Begin with “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” Apply this procedure: Count pulls before, during and after the call. For the CCA event feed, introduce one controlled variation that exposes calling forEach in a pipeline as though it were lazy. The three values are processed immediately and the call completes with undefined. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “forEach is eager and terminal” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” should remain invariant and what observation would expose calling forEach in a pipeline as though it were lazy.
Reasoned route. Begin with “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” Apply this procedure: Count pulls before, during and after the call. For the science reading pipeline, introduce one controlled variation that exposes calling forEach in a pipeline as though it were lazy. The three values are processed immediately and the call completes with undefined. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “forEach is eager and terminal” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” should remain invariant and what observation would expose calling forEach in a pipeline as though it were lazy.
Reasoned route. Begin with “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.” Apply this procedure: Count pulls before, during and after the call. For the revision generator, introduce one controlled variation that exposes calling forEach in a pipeline as though it were lazy. The three values are processed immediately and the call completes with undefined. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals calling forEach in a pipeline as though it were lazy.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Count pulls before, during and after the call.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “forEach is eager and terminal”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.?”, and “Can you make a smaller example of calling forEach in a pipeline as though it were lazy?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Predict when console output occurs. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. It occurs during the forEach call, not during a later pull. The final output alone earns only partial credit. A strong answer connects it to “Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper.”, shows the relevant intermediate evidence and states why calling forEach in a pipeline as though it were lazy would lead to a different conclusion.
Decision and transfer
Use forEach only when immediate consumption is intended. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “forEach is eager and terminal” means the learner can predict the ordinary case, recognise calling forEach in a pipeline as though it were lazy, diagnose the earliest mistaken assumption, recover with “Count pulls before, during and after the call.” and defend this decision: Use forEach only when immediate consumption is intended. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
The algorithm throws TypeError when the this value is not an object. That is the invariant to preserve when names, data or surrounding project details change.
Iterator helpers operate on iterator objects, not arbitrary scalar values. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is borrowing the method onto null or a primitive. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
Iterator.prototype.forEach.call(null, ()=>{})Explained result. The call throws before iteration. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace receiver validation before callback validation. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “The receiver must be an object”, separate contract from coincidence. The contract is “The algorithm throws TypeError when the this value is not an object.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Test only with explicit call in a disposable script.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “The receiver must be an object” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “The algorithm throws TypeError when the this value is not an object.” should remain invariant and what observation would expose borrowing the method onto null or a primitive.
Reasoned route. Begin with “The algorithm throws TypeError when the this value is not an object.” Apply this procedure: Test only with explicit call in a disposable script. For the homework result stream, introduce one controlled variation that exposes borrowing the method onto null or a primitive. The call throws before iteration. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “The receiver must be an object” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “The algorithm throws TypeError when the this value is not an object.” should remain invariant and what observation would expose borrowing the method onto null or a primitive.
Reasoned route. Begin with “The algorithm throws TypeError when the this value is not an object.” Apply this procedure: Test only with explicit call in a disposable script. For the CCA event feed, introduce one controlled variation that exposes borrowing the method onto null or a primitive. The call throws before iteration. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “The receiver must be an object” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “The algorithm throws TypeError when the this value is not an object.” should remain invariant and what observation would expose borrowing the method onto null or a primitive.
Reasoned route. Begin with “The algorithm throws TypeError when the this value is not an object.” Apply this procedure: Test only with explicit call in a disposable script. For the science reading pipeline, introduce one controlled variation that exposes borrowing the method onto null or a primitive. The call throws before iteration. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “The receiver must be an object” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “The algorithm throws TypeError when the this value is not an object.” should remain invariant and what observation would expose borrowing the method onto null or a primitive.
Reasoned route. Begin with “The algorithm throws TypeError when the this value is not an object.” Apply this procedure: Test only with explicit call in a disposable script. For the revision generator, introduce one controlled variation that exposes borrowing the method onto null or a primitive. The call throws before iteration. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals borrowing the method onto null or a primitive.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Test only with explicit call in a disposable script.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “The receiver must be an object”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests The algorithm throws TypeError when the this value is not an object.?”, and “Can you make a smaller example of borrowing the method onto null or a primitive?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Compare null with Iterator.from([]). Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The null receiver throws; the iterator receiver completes normally. The final output alone earns only partial credit. A strong answer connects it to “The algorithm throws TypeError when the this value is not an object.”, shows the relevant intermediate evidence and states why borrowing the method onto null or a primitive would lead to a different conclusion.
Decision and transfer
Adapt iterables with Iterator.from. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “The receiver must be an object” means the learner can predict the ordinary case, recognise borrowing the method onto null or a primitive, diagnose the earliest mistaken assumption, recover with “Test only with explicit call in a disposable script.” and defend this decision: Adapt iterables with Iterator.from. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm. That is the invariant to preserve when names, data or surrounding project details change.
The operation has no purpose without executable procedure logic. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is treating a missing callback as a no-op. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
Iterator.from([1,2]).forEach(null)Explained result. The call throws TypeError rather than silently ignoring the argument. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace callable check, error completion and IteratorClose. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “The procedure must be callable”, separate contract from coincidence. The contract is “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Use a custom iterator return method to observe closing.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “The procedure must be callable” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” should remain invariant and what observation would expose treating a missing callback as a no-op.
Reasoned route. Begin with “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” Apply this procedure: Use a custom iterator return method to observe closing. For the homework result stream, introduce one controlled variation that exposes treating a missing callback as a no-op. The call throws TypeError rather than silently ignoring the argument. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “The procedure must be callable” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” should remain invariant and what observation would expose treating a missing callback as a no-op.
Reasoned route. Begin with “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” Apply this procedure: Use a custom iterator return method to observe closing. For the CCA event feed, introduce one controlled variation that exposes treating a missing callback as a no-op. The call throws TypeError rather than silently ignoring the argument. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “The procedure must be callable” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” should remain invariant and what observation would expose treating a missing callback as a no-op.
Reasoned route. Begin with “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” Apply this procedure: Use a custom iterator return method to observe closing. For the science reading pipeline, introduce one controlled variation that exposes treating a missing callback as a no-op. The call throws TypeError rather than silently ignoring the argument. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “The procedure must be callable” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” should remain invariant and what observation would expose treating a missing callback as a no-op.
Reasoned route. Begin with “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.” Apply this procedure: Use a custom iterator return method to observe closing. For the revision generator, introduce one controlled variation that exposes treating a missing callback as a no-op. The call throws TypeError rather than silently ignoring the argument. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals treating a missing callback as a no-op.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Use a custom iterator return method to observe closing.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “The procedure must be callable”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.?”, and “Can you make a smaller example of treating a missing callback as a no-op?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Pass undefined in a controlled fixture. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The invalid callback produces TypeError. The final output alone earns only partial credit. A strong answer connects it to “A non-callable procedure causes TypeError and the provisional iterator record is closed according to the algorithm.”, shows the relevant intermediate evidence and states why treating a missing callback as a no-op would lead to a different conclusion.
Decision and transfer
Validate dynamic callbacks before consumption when helpful. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “The procedure must be callable” means the learner can predict the ordinary case, recognise treating a missing callback as a no-op, diagnose the earliest mistaken assumption, recover with “Use a custom iterator return method to observe closing.” and defend this decision: Validate dynamic callbacks before consumption when helpful. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
The procedure is called with the current value and a zero-based counter. That is the invariant to preserve when names, data or surrounding project details change.
The specified iterator helper supplies two callback arguments. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is expecting array, iterator or collection as a third argument. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
Iterator.from(['a','b']).forEach((value,index)=>console.log(index,value))Explained result. The output pairs 0 with a and 1 with b. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace current item and counter before increment. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “The callback receives value then index”, separate contract from coincidence. The contract is “The procedure is called with the current value and a zero-based counter.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Log arguments.length and the first two values.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “The callback receives value then index” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “The procedure is called with the current value and a zero-based counter.” should remain invariant and what observation would expose expecting array, iterator or collection as a third argument.
Reasoned route. Begin with “The procedure is called with the current value and a zero-based counter.” Apply this procedure: Log arguments.length and the first two values. For the homework result stream, introduce one controlled variation that exposes expecting array, iterator or collection as a third argument. The output pairs 0 with a and 1 with b. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “The callback receives value then index” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “The procedure is called with the current value and a zero-based counter.” should remain invariant and what observation would expose expecting array, iterator or collection as a third argument.
Reasoned route. Begin with “The procedure is called with the current value and a zero-based counter.” Apply this procedure: Log arguments.length and the first two values. For the CCA event feed, introduce one controlled variation that exposes expecting array, iterator or collection as a third argument. The output pairs 0 with a and 1 with b. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “The callback receives value then index” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “The procedure is called with the current value and a zero-based counter.” should remain invariant and what observation would expose expecting array, iterator or collection as a third argument.
Reasoned route. Begin with “The procedure is called with the current value and a zero-based counter.” Apply this procedure: Log arguments.length and the first two values. For the science reading pipeline, introduce one controlled variation that exposes expecting array, iterator or collection as a third argument. The output pairs 0 with a and 1 with b. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “The callback receives value then index” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “The procedure is called with the current value and a zero-based counter.” should remain invariant and what observation would expose expecting array, iterator or collection as a third argument.
Reasoned route. Begin with “The procedure is called with the current value and a zero-based counter.” Apply this procedure: Log arguments.length and the first two values. For the revision generator, introduce one controlled variation that exposes expecting array, iterator or collection as a third argument. The output pairs 0 with a and 1 with b. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals expecting array, iterator or collection as a third argument.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Log arguments.length and the first two values.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “The callback receives value then index”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests The procedure is called with the current value and a zero-based counter.?”, and “Can you make a smaller example of expecting array, iterator or collection as a third argument?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Write the two callback records. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. They are [a,0] and [b,1] conceptually. The final output alone earns only partial credit. A strong answer connects it to “The procedure is called with the current value and a zero-based counter.”, shows the relevant intermediate evidence and states why expecting array, iterator or collection as a third argument would lead to a different conclusion.
Decision and transfer
Capture external context explicitly when needed. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “The callback receives value then index” means the learner can predict the ordinary case, recognise expecting array, iterator or collection as a third argument, diagnose the earliest mistaken assumption, recover with “Log arguments.length and the first two values.” and defend this decision: Capture external context explicitly when needed. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
The counter begins at positive zero and increases after each successful callback. That is the invariant to preserve when names, data or surrounding project details change.
The callback index describes iteration position. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is starting labels at one without an explicit display conversion. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const seen=[]; Iterator.from([7,8]).forEach((v,i)=>seen.push(i));Explained result. seen becomes [0,1]. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace counter before and after each call. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “The index starts at zero”, separate contract from coincidence. The contract is “The counter begins at positive zero and increases after each successful callback.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Separate internal index from human-facing numbering.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “The index starts at zero” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “The counter begins at positive zero and increases after each successful callback.” should remain invariant and what observation would expose starting labels at one without an explicit display conversion.
Reasoned route. Begin with “The counter begins at positive zero and increases after each successful callback.” Apply this procedure: Separate internal index from human-facing numbering. For the homework result stream, introduce one controlled variation that exposes starting labels at one without an explicit display conversion. seen becomes [0,1]. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “The index starts at zero” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “The counter begins at positive zero and increases after each successful callback.” should remain invariant and what observation would expose starting labels at one without an explicit display conversion.
Reasoned route. Begin with “The counter begins at positive zero and increases after each successful callback.” Apply this procedure: Separate internal index from human-facing numbering. For the CCA event feed, introduce one controlled variation that exposes starting labels at one without an explicit display conversion. seen becomes [0,1]. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “The index starts at zero” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “The counter begins at positive zero and increases after each successful callback.” should remain invariant and what observation would expose starting labels at one without an explicit display conversion.
Reasoned route. Begin with “The counter begins at positive zero and increases after each successful callback.” Apply this procedure: Separate internal index from human-facing numbering. For the science reading pipeline, introduce one controlled variation that exposes starting labels at one without an explicit display conversion. seen becomes [0,1]. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “The index starts at zero” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “The counter begins at positive zero and increases after each successful callback.” should remain invariant and what observation would expose starting labels at one without an explicit display conversion.
Reasoned route. Begin with “The counter begins at positive zero and increases after each successful callback.” Apply this procedure: Separate internal index from human-facing numbering. For the revision generator, introduce one controlled variation that exposes starting labels at one without an explicit display conversion. seen becomes [0,1]. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals starting labels at one without an explicit display conversion.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Separate internal index from human-facing numbering.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “The index starts at zero”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests The counter begins at positive zero and increases after each successful callback.?”, and “Can you make a smaller example of starting labels at one without an explicit display conversion?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Create Chapter labels from the callback index. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Use i+1 only for the displayed label. The final output alone earns only partial credit. A strong answer connects it to “The counter begins at positive zero and increases after each successful callback.”, shows the relevant intermediate evidence and states why starting labels at one without an explicit display conversion would lead to a different conclusion.
Decision and transfer
Keep data indices and presentation numbers distinct. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “The index starts at zero” means the learner can predict the ordinary case, recognise starting labels at one without an explicit display conversion, diagnose the earliest mistaken assumption, recover with “Separate internal index from human-facing numbering.” and defend this decision: Keep data indices and presentation numbers distinct. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
When the source is done, forEach returns undefined. That is the invariant to preserve when names, data or surrounding project details change.
forEach is for effects, not transformation output. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is expecting an array of callback results. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const result=Iterator.from([1]).forEach(()=>42); console.log(result)Explained result. The printed result is undefined; callback return values are not collected. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace callback completion separately from method return. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Normal completion returns undefined”, separate contract from coincidence. The contract is “When the source is done, forEach returns undefined.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Return distinctive callback values and inspect the outer result.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Normal completion returns undefined” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “When the source is done, forEach returns undefined.” should remain invariant and what observation would expose expecting an array of callback results.
Reasoned route. Begin with “When the source is done, forEach returns undefined.” Apply this procedure: Return distinctive callback values and inspect the outer result. For the homework result stream, introduce one controlled variation that exposes expecting an array of callback results. The printed result is undefined; callback return values are not collected. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Normal completion returns undefined” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “When the source is done, forEach returns undefined.” should remain invariant and what observation would expose expecting an array of callback results.
Reasoned route. Begin with “When the source is done, forEach returns undefined.” Apply this procedure: Return distinctive callback values and inspect the outer result. For the CCA event feed, introduce one controlled variation that exposes expecting an array of callback results. The printed result is undefined; callback return values are not collected. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Normal completion returns undefined” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “When the source is done, forEach returns undefined.” should remain invariant and what observation would expose expecting an array of callback results.
Reasoned route. Begin with “When the source is done, forEach returns undefined.” Apply this procedure: Return distinctive callback values and inspect the outer result. For the science reading pipeline, introduce one controlled variation that exposes expecting an array of callback results. The printed result is undefined; callback return values are not collected. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Normal completion returns undefined” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “When the source is done, forEach returns undefined.” should remain invariant and what observation would expose expecting an array of callback results.
Reasoned route. Begin with “When the source is done, forEach returns undefined.” Apply this procedure: Return distinctive callback values and inspect the outer result. For the revision generator, introduce one controlled variation that exposes expecting an array of callback results. The printed result is undefined; callback return values are not collected. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals expecting an array of callback results.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Return distinctive callback values and inspect the outer result.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Normal completion returns undefined”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests When the source is done, forEach returns undefined.?”, and “Can you make a smaller example of expecting an array of callback results?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Compare forEach with map(…).toArray(). Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. forEach returns undefined; map creates transformed values for collection. The final output alone earns only partial credit. A strong answer connects it to “When the source is done, forEach returns undefined.”, shows the relevant intermediate evidence and states why expecting an array of callback results would lead to a different conclusion.
Decision and transfer
Choose map when results are the job. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Normal completion returns undefined” means the learner can predict the ordinary case, recognise expecting an array of callback results, diagnose the earliest mistaken assumption, recover with “Return distinctive callback values and inspect the outer result.” and defend this decision: Choose map when results are the job. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
A normal value returned by the procedure does not stop iteration or become output. That is the invariant to preserve when names, data or surrounding project details change.
The algorithm checks abrupt completion, not callback truthiness. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is using return false as a break statement. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const seen=[]; Iterator.from([1,2,3]).forEach(x=>{seen.push(x); return false;})Explained result. All three values are seen because false has no stopping meaning here. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace normal callback completion and next iteration. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Callback return values are ignored”, separate contract from coincidence. The contract is “A normal value returned by the procedure does not stop iteration or become output.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Run three values and inspect the full log.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Callback return values are ignored” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “A normal value returned by the procedure does not stop iteration or become output.” should remain invariant and what observation would expose using return false as a break statement.
Reasoned route. Begin with “A normal value returned by the procedure does not stop iteration or become output.” Apply this procedure: Run three values and inspect the full log. For the homework result stream, introduce one controlled variation that exposes using return false as a break statement. All three values are seen because false has no stopping meaning here. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Callback return values are ignored” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “A normal value returned by the procedure does not stop iteration or become output.” should remain invariant and what observation would expose using return false as a break statement.
Reasoned route. Begin with “A normal value returned by the procedure does not stop iteration or become output.” Apply this procedure: Run three values and inspect the full log. For the CCA event feed, introduce one controlled variation that exposes using return false as a break statement. All three values are seen because false has no stopping meaning here. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Callback return values are ignored” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “A normal value returned by the procedure does not stop iteration or become output.” should remain invariant and what observation would expose using return false as a break statement.
Reasoned route. Begin with “A normal value returned by the procedure does not stop iteration or become output.” Apply this procedure: Run three values and inspect the full log. For the science reading pipeline, introduce one controlled variation that exposes using return false as a break statement. All three values are seen because false has no stopping meaning here. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Callback return values are ignored” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “A normal value returned by the procedure does not stop iteration or become output.” should remain invariant and what observation would expose using return false as a break statement.
Reasoned route. Begin with “A normal value returned by the procedure does not stop iteration or become output.” Apply this procedure: Run three values and inspect the full log. For the revision generator, introduce one controlled variation that exposes using return false as a break statement. All three values are seen because false has no stopping meaning here. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals using return false as a break statement.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Run three values and inspect the full log.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Callback return values are ignored”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests A normal value returned by the procedure does not stop iteration or become output.?”, and “Can you make a smaller example of using return false as a break statement?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Predict seen. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. It is [1,2,3]. The final output alone earns only partial credit. A strong answer connects it to “A normal value returned by the procedure does not stop iteration or become output.”, shows the relevant intermediate evidence and states why using return false as a break statement would lead to a different conclusion.
Decision and transfer
Use for-of or another method when early normal exit is required. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Callback return values are ignored” means the learner can predict the ordinary case, recognise using return false as a break statement, diagnose the earliest mistaken assumption, recover with “Run three values and inspect the full log.” and defend this decision: Use for-of or another method when early normal exit is required. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
If the procedure throws, the abrupt completion is propagated with IteratorClose. That is the invariant to preserve when names, data or surrounding project details change.
Only an actual abrupt completion reaches this close route. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is catching inside the callback and assuming the iterator closed. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
function* g(){try{yield 1;yield 2;}finally{console.log('closed')}}
g().forEach(()=>{throw new Error('stop')})Explained result. The error propagates and generator cleanup runs. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace yield, throw, IteratorClose and finally. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “A callback throw closes the iterator”, separate contract from coincidence. The contract is “If the procedure throws, the abrupt completion is propagated with IteratorClose.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Use try/finally and catch outside for observation.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “A callback throw closes the iterator” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “If the procedure throws, the abrupt completion is propagated with IteratorClose.” should remain invariant and what observation would expose catching inside the callback and assuming the iterator closed.
Reasoned route. Begin with “If the procedure throws, the abrupt completion is propagated with IteratorClose.” Apply this procedure: Use try/finally and catch outside for observation. For the homework result stream, introduce one controlled variation that exposes catching inside the callback and assuming the iterator closed. The error propagates and generator cleanup runs. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “A callback throw closes the iterator” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “If the procedure throws, the abrupt completion is propagated with IteratorClose.” should remain invariant and what observation would expose catching inside the callback and assuming the iterator closed.
Reasoned route. Begin with “If the procedure throws, the abrupt completion is propagated with IteratorClose.” Apply this procedure: Use try/finally and catch outside for observation. For the CCA event feed, introduce one controlled variation that exposes catching inside the callback and assuming the iterator closed. The error propagates and generator cleanup runs. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “A callback throw closes the iterator” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “If the procedure throws, the abrupt completion is propagated with IteratorClose.” should remain invariant and what observation would expose catching inside the callback and assuming the iterator closed.
Reasoned route. Begin with “If the procedure throws, the abrupt completion is propagated with IteratorClose.” Apply this procedure: Use try/finally and catch outside for observation. For the science reading pipeline, introduce one controlled variation that exposes catching inside the callback and assuming the iterator closed. The error propagates and generator cleanup runs. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “A callback throw closes the iterator” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “If the procedure throws, the abrupt completion is propagated with IteratorClose.” should remain invariant and what observation would expose catching inside the callback and assuming the iterator closed.
Reasoned route. Begin with “If the procedure throws, the abrupt completion is propagated with IteratorClose.” Apply this procedure: Use try/finally and catch outside for observation. For the revision generator, introduce one controlled variation that exposes catching inside the callback and assuming the iterator closed. The error propagates and generator cleanup runs. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals catching inside the callback and assuming the iterator closed.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Use try/finally and catch outside for observation.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “A callback throw closes the iterator”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests If the procedure throws, the abrupt completion is propagated with IteratorClose.?”, and “Can you make a smaller example of catching inside the callback and assuming the iterator closed?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Explain the event order. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The first value reaches the callback, it throws, cleanup runs, then the outer catch receives the error. The final output alone earns only partial credit. A strong answer connects it to “If the procedure throws, the abrupt completion is propagated with IteratorClose.”, shows the relevant intermediate evidence and states why catching inside the callback and assuming the iterator closed would lead to a different conclusion.
Decision and transfer
Catch only where recovery is meaningful. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “A callback throw closes the iterator” means the learner can predict the ordinary case, recognise catching inside the callback and assuming the iterator closed, diagnose the earliest mistaken assumption, recover with “Use try/finally and catch outside for observation.” and defend this decision: Catch only where recovery is meaningful. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
If stepping the source throws, forEach does not convert it into normal completion. That is the invariant to preserve when names, data or surrounding project details change.
Source failure and normal done are different completions. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is reporting a partially processed sequence as success. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const bad=Iterator.from({[Symbol.iterator](){return {next(){throw new Error('source')}}}}); bad.forEach(()=>{})Explained result. The consumer receives the source error. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace successful callbacks then the failing next. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Source errors propagate”, separate contract from coincidence. The contract is “If stepping the source throws, forEach does not convert it into normal completion.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Use a custom iterator whose next throws after a logged value.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Source errors propagate” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “If stepping the source throws, forEach does not convert it into normal completion.” should remain invariant and what observation would expose reporting a partially processed sequence as success.
Reasoned route. Begin with “If stepping the source throws, forEach does not convert it into normal completion.” Apply this procedure: Use a custom iterator whose next throws after a logged value. For the homework result stream, introduce one controlled variation that exposes reporting a partially processed sequence as success. The consumer receives the source error. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Source errors propagate” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “If stepping the source throws, forEach does not convert it into normal completion.” should remain invariant and what observation would expose reporting a partially processed sequence as success.
Reasoned route. Begin with “If stepping the source throws, forEach does not convert it into normal completion.” Apply this procedure: Use a custom iterator whose next throws after a logged value. For the CCA event feed, introduce one controlled variation that exposes reporting a partially processed sequence as success. The consumer receives the source error. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Source errors propagate” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “If stepping the source throws, forEach does not convert it into normal completion.” should remain invariant and what observation would expose reporting a partially processed sequence as success.
Reasoned route. Begin with “If stepping the source throws, forEach does not convert it into normal completion.” Apply this procedure: Use a custom iterator whose next throws after a logged value. For the science reading pipeline, introduce one controlled variation that exposes reporting a partially processed sequence as success. The consumer receives the source error. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Source errors propagate” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “If stepping the source throws, forEach does not convert it into normal completion.” should remain invariant and what observation would expose reporting a partially processed sequence as success.
Reasoned route. Begin with “If stepping the source throws, forEach does not convert it into normal completion.” Apply this procedure: Use a custom iterator whose next throws after a logged value. For the revision generator, introduce one controlled variation that exposes reporting a partially processed sequence as success. The consumer receives the source error. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals reporting a partially processed sequence as success.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Use a custom iterator whose next throws after a logged value.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Source errors propagate”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests If stepping the source throws, forEach does not convert it into normal completion.?”, and “Can you make a smaller example of reporting a partially processed sequence as success?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Record partial effects and final error. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Earlier side effects can remain even though the call fails. The final output alone earns only partial credit. A strong answer connects it to “If stepping the source throws, forEach does not convert it into normal completion.”, shows the relevant intermediate evidence and states why reporting a partially processed sequence as success would lead to a different conclusion.
Decision and transfer
Design effects to tolerate partial progress. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Source errors propagate” means the learner can predict the ordinary case, recognise reporting a partially processed sequence as success, diagnose the earliest mistaken assumption, recover with “Use a custom iterator whose next throws after a logged value.” and defend this decision: Design effects to tolerate partial progress. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
A completed forEach consumes the source through done. That is the invariant to preserve when names, data or surrounding project details change.
Iterators carry a cursor rather than replayable collection state. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is trying to reuse one iterator for a second report. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const it=Iterator.from([1,2]); it.forEach(()=>{}); console.log(it.next())Explained result. The later next reports done true. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace one shared cursor. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “The iterator is exhausted after completion”, separate contract from coincidence. The contract is “A completed forEach consumes the source through done.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Inspect next before and after consumption in separate fixtures.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “The iterator is exhausted after completion” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “A completed forEach consumes the source through done.” should remain invariant and what observation would expose trying to reuse one iterator for a second report.
Reasoned route. Begin with “A completed forEach consumes the source through done.” Apply this procedure: Inspect next before and after consumption in separate fixtures. For the homework result stream, introduce one controlled variation that exposes trying to reuse one iterator for a second report. The later next reports done true. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “The iterator is exhausted after completion” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “A completed forEach consumes the source through done.” should remain invariant and what observation would expose trying to reuse one iterator for a second report.
Reasoned route. Begin with “A completed forEach consumes the source through done.” Apply this procedure: Inspect next before and after consumption in separate fixtures. For the CCA event feed, introduce one controlled variation that exposes trying to reuse one iterator for a second report. The later next reports done true. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “The iterator is exhausted after completion” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “A completed forEach consumes the source through done.” should remain invariant and what observation would expose trying to reuse one iterator for a second report.
Reasoned route. Begin with “A completed forEach consumes the source through done.” Apply this procedure: Inspect next before and after consumption in separate fixtures. For the science reading pipeline, introduce one controlled variation that exposes trying to reuse one iterator for a second report. The later next reports done true. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “The iterator is exhausted after completion” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “A completed forEach consumes the source through done.” should remain invariant and what observation would expose trying to reuse one iterator for a second report.
Reasoned route. Begin with “A completed forEach consumes the source through done.” Apply this procedure: Inspect next before and after consumption in separate fixtures. For the revision generator, introduce one controlled variation that exposes trying to reuse one iterator for a second report. The later next reports done true. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals trying to reuse one iterator for a second report.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Inspect next before and after consumption in separate fixtures.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “The iterator is exhausted after completion”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests A completed forEach consumes the source through done.?”, and “Can you make a smaller example of trying to reuse one iterator for a second report?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Run two forEach calls on one iterator. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The second sees no values after the first exhausts it. The final output alone earns only partial credit. A strong answer connects it to “A completed forEach consumes the source through done.”, shows the relevant intermediate evidence and states why trying to reuse one iterator for a second report would lead to a different conclusion.
Decision and transfer
Create a fresh iterator for independent passes. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “The iterator is exhausted after completion” means the learner can predict the ordinary case, recognise trying to reuse one iterator for a second report, diagnose the earliest mistaken assumption, recover with “Inspect next before and after consumption in separate fixtures.” and defend this decision: Create a fresh iterator for independent passes. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
Interleaving direct next calls and forEach changes which values forEach receives. That is the invariant to preserve when names, data or surrounding project details change.
The method continues from current iterator state. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is assuming forEach starts from the original beginning. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const it=Iterator.from([1,2,3]); console.log(it.next()); it.forEach(console.log)Explained result. The direct pull takes 1; forEach processes 2 and 3. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace direct consumer then terminal consumer. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Shared consumers observe shared advancement”, separate contract from coincidence. The contract is “Interleaving direct next calls and forEach changes which values forEach receives.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Draw the cursor after every pull.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Shared consumers observe shared advancement” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Interleaving direct next calls and forEach changes which values forEach receives.” should remain invariant and what observation would expose assuming forEach starts from the original beginning.
Reasoned route. Begin with “Interleaving direct next calls and forEach changes which values forEach receives.” Apply this procedure: Draw the cursor after every pull. For the homework result stream, introduce one controlled variation that exposes assuming forEach starts from the original beginning. The direct pull takes 1; forEach processes 2 and 3. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Shared consumers observe shared advancement” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Interleaving direct next calls and forEach changes which values forEach receives.” should remain invariant and what observation would expose assuming forEach starts from the original beginning.
Reasoned route. Begin with “Interleaving direct next calls and forEach changes which values forEach receives.” Apply this procedure: Draw the cursor after every pull. For the CCA event feed, introduce one controlled variation that exposes assuming forEach starts from the original beginning. The direct pull takes 1; forEach processes 2 and 3. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Shared consumers observe shared advancement” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Interleaving direct next calls and forEach changes which values forEach receives.” should remain invariant and what observation would expose assuming forEach starts from the original beginning.
Reasoned route. Begin with “Interleaving direct next calls and forEach changes which values forEach receives.” Apply this procedure: Draw the cursor after every pull. For the science reading pipeline, introduce one controlled variation that exposes assuming forEach starts from the original beginning. The direct pull takes 1; forEach processes 2 and 3. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Shared consumers observe shared advancement” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Interleaving direct next calls and forEach changes which values forEach receives.” should remain invariant and what observation would expose assuming forEach starts from the original beginning.
Reasoned route. Begin with “Interleaving direct next calls and forEach changes which values forEach receives.” Apply this procedure: Draw the cursor after every pull. For the revision generator, introduce one controlled variation that exposes assuming forEach starts from the original beginning. The direct pull takes 1; forEach processes 2 and 3. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals assuming forEach starts from the original beginning.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Draw the cursor after every pull.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Shared consumers observe shared advancement”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Interleaving direct next calls and forEach changes which values forEach receives.?”, and “Can you make a smaller example of assuming forEach starts from the original beginning?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Predict callback values. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. They are 2 and 3. The final output alone earns only partial credit. A strong answer connects it to “Interleaving direct next calls and forEach changes which values forEach receives.”, shows the relevant intermediate evidence and states why assuming forEach starts from the original beginning would lead to a different conclusion.
Decision and transfer
Avoid multiple consumers unless the shared cursor is deliberate. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Shared consumers observe shared advancement” means the learner can predict the ordinary case, recognise assuming forEach starts from the original beginning, diagnose the earliest mistaken assumption, recover with “Draw the cursor after every pull.” and defend this decision: Avoid multiple consumers unless the shared cursor is deliberate. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 12 OF 20 · Test the boundary
12. Array forEach and iterator forEach have different source models
Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator. That is the invariant to preserve when names, data or surrounding project details change.
Receiver model determines reuse, laziness and pipeline behaviour. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is treating matching method names as identical contracts. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const data=[1,2]; data.forEach(log); Iterator.from(data).forEach(log);Explained result. Both call a callback, but only the iterator call advances that iterator object irreversibly. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace collection storage versus iterator cursor. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Array forEach and iterator forEach have different source models”, separate contract from coincidence. The contract is “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Compare a fresh array iterator after each run.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Array forEach and iterator forEach have different source models” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” should remain invariant and what observation would expose treating matching method names as identical contracts.
Reasoned route. Begin with “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” Apply this procedure: Compare a fresh array iterator after each run. For the homework result stream, introduce one controlled variation that exposes treating matching method names as identical contracts. Both call a callback, but only the iterator call advances that iterator object irreversibly. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Array forEach and iterator forEach have different source models” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” should remain invariant and what observation would expose treating matching method names as identical contracts.
Reasoned route. Begin with “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” Apply this procedure: Compare a fresh array iterator after each run. For the CCA event feed, introduce one controlled variation that exposes treating matching method names as identical contracts. Both call a callback, but only the iterator call advances that iterator object irreversibly. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Array forEach and iterator forEach have different source models” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” should remain invariant and what observation would expose treating matching method names as identical contracts.
Reasoned route. Begin with “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” Apply this procedure: Compare a fresh array iterator after each run. For the science reading pipeline, introduce one controlled variation that exposes treating matching method names as identical contracts. Both call a callback, but only the iterator call advances that iterator object irreversibly. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Array forEach and iterator forEach have different source models” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” should remain invariant and what observation would expose treating matching method names as identical contracts.
Reasoned route. Begin with “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.” Apply this procedure: Compare a fresh array iterator after each run. For the revision generator, introduce one controlled variation that exposes treating matching method names as identical contracts. Both call a callback, but only the iterator call advances that iterator object irreversibly. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals treating matching method names as identical contracts.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Compare a fresh array iterator after each run.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Array forEach and iterator forEach have different source models”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.?”, and “Can you make a smaller example of treating matching method names as identical contracts?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Explain which source can be iterated afresh. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The array can create another iterator; the consumed iterator itself cannot replay. The final output alone earns only partial credit. A strong answer connects it to “Array.prototype.forEach traverses an array, while Iterator.prototype.forEach consumes a one-shot iterator.”, shows the relevant intermediate evidence and states why treating matching method names as identical contracts would lead to a different conclusion.
Decision and transfer
Name the source model in code review. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Array forEach and iterator forEach have different source models” means the learner can predict the ordinary case, recognise treating matching method names as identical contracts, diagnose the earliest mistaken assumption, recover with “Compare a fresh array iterator after each run.” and defend this decision: Name the source model in code review. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 13 OF 20 · Diagnose and repair
13. forEach does not work safely on an unbounded iterator
Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws. That is the invariant to preserve when names, data or surrounding project details change.
Eager exhaustion has no completion point on an endless source. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is running the example and freezing the process. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
function* naturals(){let n=0;while(true)yield n++;}
// naturals().forEach(...) would not finishExplained result. An unbounded source needs a bound before the terminal consumer. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace finite helper boundary then terminal consumption. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “forEach does not work safely on an unbounded iterator”, separate contract from coincidence. The contract is “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Apply take(n) before forEach in the safe fixture.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “forEach does not work safely on an unbounded iterator” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” should remain invariant and what observation would expose running the example and freezing the process.
Reasoned route. Begin with “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” Apply this procedure: Apply take(n) before forEach in the safe fixture. For the homework result stream, introduce one controlled variation that exposes running the example and freezing the process. An unbounded source needs a bound before the terminal consumer. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “forEach does not work safely on an unbounded iterator” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” should remain invariant and what observation would expose running the example and freezing the process.
Reasoned route. Begin with “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” Apply this procedure: Apply take(n) before forEach in the safe fixture. For the CCA event feed, introduce one controlled variation that exposes running the example and freezing the process. An unbounded source needs a bound before the terminal consumer. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “forEach does not work safely on an unbounded iterator” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” should remain invariant and what observation would expose running the example and freezing the process.
Reasoned route. Begin with “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” Apply this procedure: Apply take(n) before forEach in the safe fixture. For the science reading pipeline, introduce one controlled variation that exposes running the example and freezing the process. An unbounded source needs a bound before the terminal consumer. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “forEach does not work safely on an unbounded iterator” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” should remain invariant and what observation would expose running the example and freezing the process.
Reasoned route. Begin with “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.” Apply this procedure: Apply take(n) before forEach in the safe fixture. For the revision generator, introduce one controlled variation that exposes running the example and freezing the process. An unbounded source needs a bound before the terminal consumer. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals running the example and freezing the process.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Apply take(n) before forEach in the safe fixture.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “forEach does not work safely on an unbounded iterator”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.?”, and “Can you make a smaller example of running the example and freezing the process?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Process five natural numbers. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Use naturals().take(5).forEach(…). The final output alone earns only partial credit. A strong answer connects it to “Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws.”, shows the relevant intermediate evidence and states why running the example and freezing the process would lead to a different conclusion.
Decision and transfer
Place a real bound before any eager consumer. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “forEach does not work safely on an unbounded iterator” means the learner can predict the ordinary case, recognise running the example and freezing the process, diagnose the earliest mistaken assumption, recover with “Apply take(n) before forEach in the safe fixture.” and defend this decision: Place a real bound before any eager consumer. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
A finite take helper can make a prefix safe for terminal forEach consumption. That is the invariant to preserve when names, data or surrounding project details change.
forEach returns undefined, leaving no helper on which to call take. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is putting take after forEach. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
naturals().take(5).forEach((v,i)=>console.log(i,v))Explained result. Exactly five callbacks occur and the bounded helper closes its source. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace source, take remaining count and callback. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “take can bound forEach safely”, separate contract from coincidence. The contract is “A finite take helper can make a prefix safe for terminal forEach consumption.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Write the pipeline left to right before running.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “take can bound forEach safely” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “A finite take helper can make a prefix safe for terminal forEach consumption.” should remain invariant and what observation would expose putting take after forEach.
Reasoned route. Begin with “A finite take helper can make a prefix safe for terminal forEach consumption.” Apply this procedure: Write the pipeline left to right before running. For the homework result stream, introduce one controlled variation that exposes putting take after forEach. Exactly five callbacks occur and the bounded helper closes its source. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “take can bound forEach safely” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “A finite take helper can make a prefix safe for terminal forEach consumption.” should remain invariant and what observation would expose putting take after forEach.
Reasoned route. Begin with “A finite take helper can make a prefix safe for terminal forEach consumption.” Apply this procedure: Write the pipeline left to right before running. For the CCA event feed, introduce one controlled variation that exposes putting take after forEach. Exactly five callbacks occur and the bounded helper closes its source. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “take can bound forEach safely” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “A finite take helper can make a prefix safe for terminal forEach consumption.” should remain invariant and what observation would expose putting take after forEach.
Reasoned route. Begin with “A finite take helper can make a prefix safe for terminal forEach consumption.” Apply this procedure: Write the pipeline left to right before running. For the science reading pipeline, introduce one controlled variation that exposes putting take after forEach. Exactly five callbacks occur and the bounded helper closes its source. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “take can bound forEach safely” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “A finite take helper can make a prefix safe for terminal forEach consumption.” should remain invariant and what observation would expose putting take after forEach.
Reasoned route. Begin with “A finite take helper can make a prefix safe for terminal forEach consumption.” Apply this procedure: Write the pipeline left to right before running. For the revision generator, introduce one controlled variation that exposes putting take after forEach. Exactly five callbacks occur and the bounded helper closes its source. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals putting take after forEach.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Write the pipeline left to right before running.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “take can bound forEach safely”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests A finite take helper can make a prefix safe for terminal forEach consumption.?”, and “Can you make a smaller example of putting take after forEach?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Compare two orderings. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Only take(…).forEach(…) forms a valid bounded pipeline. The final output alone earns only partial credit. A strong answer connects it to “A finite take helper can make a prefix safe for terminal forEach consumption.”, shows the relevant intermediate evidence and states why putting take after forEach would lead to a different conclusion.
Decision and transfer
Bound early enough to control work. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “take can bound forEach safely” means the learner can predict the ordinary case, recognise putting take after forEach, diagnose the earliest mistaken assumption, recover with “Write the pipeline left to right before running.” and defend this decision: Bound early enough to control work. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 15 OF 20 · Diagnose and repair
15. forEach is appropriate for controlled side effects
The method is clearest when every consumed value should trigger an effect such as logging to a test sink. That is the invariant to preserve when names, data or surrounding project details change.
Partial progress can survive a later error. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is performing irreversible external writes without retry design. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const log=[]; Iterator.from(['a','b']).forEach((v,i)=>log.push({i,v}))Explained result. The sink receives one record per value in order. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace effect record and failure boundary. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “forEach is appropriate for controlled side effects”, separate contract from coincidence. The contract is “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Use in-memory fixtures before external effects.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “forEach is appropriate for controlled side effects” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” should remain invariant and what observation would expose performing irreversible external writes without retry design.
Reasoned route. Begin with “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” Apply this procedure: Use in-memory fixtures before external effects. For the homework result stream, introduce one controlled variation that exposes performing irreversible external writes without retry design. The sink receives one record per value in order. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “forEach is appropriate for controlled side effects” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” should remain invariant and what observation would expose performing irreversible external writes without retry design.
Reasoned route. Begin with “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” Apply this procedure: Use in-memory fixtures before external effects. For the CCA event feed, introduce one controlled variation that exposes performing irreversible external writes without retry design. The sink receives one record per value in order. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “forEach is appropriate for controlled side effects” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” should remain invariant and what observation would expose performing irreversible external writes without retry design.
Reasoned route. Begin with “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” Apply this procedure: Use in-memory fixtures before external effects. For the science reading pipeline, introduce one controlled variation that exposes performing irreversible external writes without retry design. The sink receives one record per value in order. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “forEach is appropriate for controlled side effects” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” should remain invariant and what observation would expose performing irreversible external writes without retry design.
Reasoned route. Begin with “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.” Apply this procedure: Use in-memory fixtures before external effects. For the revision generator, introduce one controlled variation that exposes performing irreversible external writes without retry design. The sink receives one record per value in order. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals performing irreversible external writes without retry design.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Use in-memory fixtures before external effects.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “forEach is appropriate for controlled side effects”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests The method is clearest when every consumed value should trigger an effect such as logging to a test sink.?”, and “Can you make a smaller example of performing irreversible external writes without retry design?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Design idempotent keys from index and value identity. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. A retry can then recognise already-applied work. The final output alone earns only partial credit. A strong answer connects it to “The method is clearest when every consumed value should trigger an effect such as logging to a test sink.”, shows the relevant intermediate evidence and states why performing irreversible external writes without retry design would lead to a different conclusion.
Decision and transfer
Prefer effects that can be checked or retried. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “forEach is appropriate for controlled side effects” means the learner can predict the ordinary case, recognise performing irreversible external writes without retry design, diagnose the earliest mistaken assumption, recover with “Use in-memory fixtures before external effects.” and defend this decision: Prefer effects that can be checked or retried. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 16 OF 20 · Diagnose and repair
16. Async work is not awaited by synchronous forEach
A synchronous iterator forEach does not await promises returned by its callback. That is the invariant to preserve when names, data or surrounding project details change.
Normal promise objects are merely ignored callback return values. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is assuming async callback makes the consumer asynchronous. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
Iterator.from([1,2]).forEach(async x=>{await save(x)})Explained result. The method returns undefined after starting callbacks according to synchronous iteration; promise completion needs separate coordination. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace callback return Promise and outer method completion. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Async work is not awaited by synchronous forEach”, separate contract from coincidence. The contract is “A synchronous iterator forEach does not await promises returned by its callback.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Use an explicit for-of loop with await inside an async function.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Async work is not awaited by synchronous forEach” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “A synchronous iterator forEach does not await promises returned by its callback.” should remain invariant and what observation would expose assuming async callback makes the consumer asynchronous.
Reasoned route. Begin with “A synchronous iterator forEach does not await promises returned by its callback.” Apply this procedure: Use an explicit for-of loop with await inside an async function. For the homework result stream, introduce one controlled variation that exposes assuming async callback makes the consumer asynchronous. The method returns undefined after starting callbacks according to synchronous iteration; promise completion needs separate coordination. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Async work is not awaited by synchronous forEach” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “A synchronous iterator forEach does not await promises returned by its callback.” should remain invariant and what observation would expose assuming async callback makes the consumer asynchronous.
Reasoned route. Begin with “A synchronous iterator forEach does not await promises returned by its callback.” Apply this procedure: Use an explicit for-of loop with await inside an async function. For the CCA event feed, introduce one controlled variation that exposes assuming async callback makes the consumer asynchronous. The method returns undefined after starting callbacks according to synchronous iteration; promise completion needs separate coordination. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Async work is not awaited by synchronous forEach” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “A synchronous iterator forEach does not await promises returned by its callback.” should remain invariant and what observation would expose assuming async callback makes the consumer asynchronous.
Reasoned route. Begin with “A synchronous iterator forEach does not await promises returned by its callback.” Apply this procedure: Use an explicit for-of loop with await inside an async function. For the science reading pipeline, introduce one controlled variation that exposes assuming async callback makes the consumer asynchronous. The method returns undefined after starting callbacks according to synchronous iteration; promise completion needs separate coordination. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Async work is not awaited by synchronous forEach” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “A synchronous iterator forEach does not await promises returned by its callback.” should remain invariant and what observation would expose assuming async callback makes the consumer asynchronous.
Reasoned route. Begin with “A synchronous iterator forEach does not await promises returned by its callback.” Apply this procedure: Use an explicit for-of loop with await inside an async function. For the revision generator, introduce one controlled variation that exposes assuming async callback makes the consumer asynchronous. The method returns undefined after starting callbacks according to synchronous iteration; promise completion needs separate coordination. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals assuming async callback makes the consumer asynchronous.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Use an explicit for-of loop with await inside an async function.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Async work is not awaited by synchronous forEach”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests A synchronous iterator forEach does not await promises returned by its callback.?”, and “Can you make a smaller example of assuming async callback makes the consumer asynchronous?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Rewrite sequential asynchronous work. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Use for (const value of iterator) await save(value). The final output alone earns only partial credit. A strong answer connects it to “A synchronous iterator forEach does not await promises returned by its callback.”, shows the relevant intermediate evidence and states why assuming async callback makes the consumer asynchronous would lead to a different conclusion.
Decision and transfer
Choose control flow that expresses awaiting. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Async work is not awaited by synchronous forEach” means the learner can predict the ordinary case, recognise assuming async callback makes the consumer asynchronous, diagnose the earliest mistaken assumption, recover with “Use an explicit for-of loop with await inside an async function.” and defend this decision: Choose control flow that expresses awaiting. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 17 OF 20 · Transfer with judgment
17. Mutation of external state needs a clear invariant
Callback effects should preserve a stated rule after every processed value. That is the invariant to preserve when names, data or surrounding project details change.
State may be partly updated when a later callback throws. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is reading the final total without considering partial failure. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
let total=0; Iterator.from([2,3]).forEach(x=>{total+=x})Explained result. total becomes 5 after full normal completion. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace state before and after each callback. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Mutation of external state needs a clear invariant”, separate contract from coincidence. The contract is “Callback effects should preserve a stated rule after every processed value.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Define whether partial totals are acceptable or transactional recovery is needed.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Mutation of external state needs a clear invariant” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Callback effects should preserve a stated rule after every processed value.” should remain invariant and what observation would expose reading the final total without considering partial failure.
Reasoned route. Begin with “Callback effects should preserve a stated rule after every processed value.” Apply this procedure: Define whether partial totals are acceptable or transactional recovery is needed. For the homework result stream, introduce one controlled variation that exposes reading the final total without considering partial failure. total becomes 5 after full normal completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Mutation of external state needs a clear invariant” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Callback effects should preserve a stated rule after every processed value.” should remain invariant and what observation would expose reading the final total without considering partial failure.
Reasoned route. Begin with “Callback effects should preserve a stated rule after every processed value.” Apply this procedure: Define whether partial totals are acceptable or transactional recovery is needed. For the CCA event feed, introduce one controlled variation that exposes reading the final total without considering partial failure. total becomes 5 after full normal completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Mutation of external state needs a clear invariant” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Callback effects should preserve a stated rule after every processed value.” should remain invariant and what observation would expose reading the final total without considering partial failure.
Reasoned route. Begin with “Callback effects should preserve a stated rule after every processed value.” Apply this procedure: Define whether partial totals are acceptable or transactional recovery is needed. For the science reading pipeline, introduce one controlled variation that exposes reading the final total without considering partial failure. total becomes 5 after full normal completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Mutation of external state needs a clear invariant” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Callback effects should preserve a stated rule after every processed value.” should remain invariant and what observation would expose reading the final total without considering partial failure.
Reasoned route. Begin with “Callback effects should preserve a stated rule after every processed value.” Apply this procedure: Define whether partial totals are acceptable or transactional recovery is needed. For the revision generator, introduce one controlled variation that exposes reading the final total without considering partial failure. total becomes 5 after full normal completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals reading the final total without considering partial failure.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Define whether partial totals are acceptable or transactional recovery is needed.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Mutation of external state needs a clear invariant”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Callback effects should preserve a stated rule after every processed value.?”, and “Can you make a smaller example of reading the final total without considering partial failure?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Throw on the second value and report total. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The first effect remains unless application code rolls it back. The final output alone earns only partial credit. A strong answer connects it to “Callback effects should preserve a stated rule after every processed value.”, shows the relevant intermediate evidence and states why reading the final total without considering partial failure would lead to a different conclusion.
Decision and transfer
Make partial-state policy explicit. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Mutation of external state needs a clear invariant” means the learner can predict the ordinary case, recognise reading the final total without considering partial failure, diagnose the earliest mistaken assumption, recover with “Define whether partial totals are acceptable or transactional recovery is needed.” and defend this decision: Make partial-state policy explicit. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
Iterator helpers require a runtime implementing Iterator.prototype.forEach. That is the invariant to preserve when names, data or surrounding project details change.
Deployment support and language contract are separate facts. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is diagnosing missing API as callback logic failure. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
console.log(typeof Iterator, typeof Iterator?.prototype?.forEach)Explained result. A supporting environment reports the functions; an older one may not. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace specification, implementation and application requirement. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “Runtime support is versioned”, separate contract from coincidence. The contract is “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Record engine name and version before selecting a compatibility route.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “Runtime support is versioned” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” should remain invariant and what observation would expose diagnosing missing API as callback logic failure.
Reasoned route. Begin with “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” Apply this procedure: Record engine name and version before selecting a compatibility route. For the homework result stream, introduce one controlled variation that exposes diagnosing missing API as callback logic failure. A supporting environment reports the functions; an older one may not. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “Runtime support is versioned” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” should remain invariant and what observation would expose diagnosing missing API as callback logic failure.
Reasoned route. Begin with “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” Apply this procedure: Record engine name and version before selecting a compatibility route. For the CCA event feed, introduce one controlled variation that exposes diagnosing missing API as callback logic failure. A supporting environment reports the functions; an older one may not. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “Runtime support is versioned” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” should remain invariant and what observation would expose diagnosing missing API as callback logic failure.
Reasoned route. Begin with “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” Apply this procedure: Record engine name and version before selecting a compatibility route. For the science reading pipeline, introduce one controlled variation that exposes diagnosing missing API as callback logic failure. A supporting environment reports the functions; an older one may not. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “Runtime support is versioned” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” should remain invariant and what observation would expose diagnosing missing API as callback logic failure.
Reasoned route. Begin with “Iterator helpers require a runtime implementing Iterator.prototype.forEach.” Apply this procedure: Record engine name and version before selecting a compatibility route. For the revision generator, introduce one controlled variation that exposes diagnosing missing API as callback logic failure. A supporting environment reports the functions; an older one may not. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals diagnosing missing API as callback logic failure.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Record engine name and version before selecting a compatibility route.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “Runtime support is versioned”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Iterator helpers require a runtime implementing Iterator.prototype.forEach.?”, and “Can you make a smaller example of diagnosing missing API as callback logic failure?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Write a feature check. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Test the method type without invoking it. The final output alone earns only partial credit. A strong answer connects it to “Iterator helpers require a runtime implementing Iterator.prototype.forEach.”, shows the relevant intermediate evidence and states why diagnosing missing API as callback logic failure would lead to a different conclusion.
Decision and transfer
Use a tested compatibility layer or declared version floor. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “Runtime support is versioned” means the learner can predict the ordinary case, recognise diagnosing missing API as callback logic failure, diagnose the earliest mistaken assumption, recover with “Record engine name and version before selecting a compatibility route.” and defend this decision: Use a tested compatibility layer or declared version floor. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 19 OF 20 · Transfer with judgment
19. forEach, map, reduce and for-of serve different jobs
forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control. That is the invariant to preserve when names, data or surrounding project details change.
Method choice communicates how results and stopping are handled. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is choosing forEach because it is familiar. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
// effects
it.forEach(effect)
// early exit
for (const x of it) { if (done(x)) break }Explained result. The right construct follows desired output, laziness and control flow. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace reader job to construct contract. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “forEach, map, reduce and for-of serve different jobs”, separate contract from coincidence. The contract is “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “State effect, output and early-exit needs first.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “forEach, map, reduce and for-of serve different jobs” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” should remain invariant and what observation would expose choosing forEach because it is familiar.
Reasoned route. Begin with “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” Apply this procedure: State effect, output and early-exit needs first. For the homework result stream, introduce one controlled variation that exposes choosing forEach because it is familiar. The right construct follows desired output, laziness and control flow. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “forEach, map, reduce and for-of serve different jobs” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” should remain invariant and what observation would expose choosing forEach because it is familiar.
Reasoned route. Begin with “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” Apply this procedure: State effect, output and early-exit needs first. For the CCA event feed, introduce one controlled variation that exposes choosing forEach because it is familiar. The right construct follows desired output, laziness and control flow. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “forEach, map, reduce and for-of serve different jobs” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” should remain invariant and what observation would expose choosing forEach because it is familiar.
Reasoned route. Begin with “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” Apply this procedure: State effect, output and early-exit needs first. For the science reading pipeline, introduce one controlled variation that exposes choosing forEach because it is familiar. The right construct follows desired output, laziness and control flow. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “forEach, map, reduce and for-of serve different jobs” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” should remain invariant and what observation would expose choosing forEach because it is familiar.
Reasoned route. Begin with “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.” Apply this procedure: State effect, output and early-exit needs first. For the revision generator, introduce one controlled variation that exposes choosing forEach because it is familiar. The right construct follows desired output, laziness and control flow. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals choosing forEach because it is familiar.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: State effect, output and early-exit needs first.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “forEach, map, reduce and for-of serve different jobs”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.?”, and “Can you make a smaller example of choosing forEach because it is familiar?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Choose for transformation, total and cancellable scan. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. Use map, reduce and for-of respectively. The final output alone earns only partial credit. A strong answer connects it to “forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control.”, shows the relevant intermediate evidence and states why choosing forEach because it is familiar would lead to a different conclusion.
Decision and transfer
Prefer the construct whose completion is easiest to explain. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “forEach, map, reduce and for-of serve different jobs” means the learner can predict the ordinary case, recognise choosing forEach because it is familiar, diagnose the earliest mistaken assumption, recover with “State effect, output and early-exit needs first.” and defend this decision: Prefer the construct whose completion is easiest to explain. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
CHAPTER 20 OF 20 · Transfer with judgment
20. A good forEach test checks effects, order and cleanup
Testing only the final count can miss wrong order or missing IteratorClose behaviour. That is the invariant to preserve when names, data or surrounding project details change.
Iterator semantics include ownership and closing. The useful question is not “Which line should I copy?” but “Which state, value or rendering decision does this operation control?” Answering that question prevents a correct-looking result from hiding a wrong mental model.
The most revealing near-miss is asserting only that the callback ran. A learner should predict the result first, run the smallest safe case, label the observation and explain the earliest point where the prediction diverges. This turns an error into evidence rather than a verdict on ability.
Core worked example
const events=[]; /* record next, callback, return and error */Explained result. A complete test can prove ordered callbacks and cleanup on abrupt completion. The explanation should identify the input, the governing rule, the observable outcome and one boundary that would change the conclusion. If any of those four parts is missing, reduce the example and trace it again.
Read the trace, not just the last line
Trace the full event ledger. Write the trace as a sequence of labelled states. For JavaScript Iterator.prototype.forEach, inspect next() calls, callback arguments, cursor position, return() cleanup and the final completion record; a single final value can hide the mechanism that produced it.
For “A good forEach test checks effects, order and cleanup”, separate contract from coincidence. The contract is “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” and comes from the primary specification or manual. Coincidence is what happened on one machine, font, filesystem or repository. Record both, then use “Use a custom iterator with logged next and return.” to decide whether another observation challenges the model or merely reflects the environment.
Four purposeful transfer cases
Case 1: homework result stream. Model “A good forEach test checks effects, order and cleanup” with a finite iterator of answers, zero-based labels and a test sink. Before touching a tool, state which part of the case corresponds to the input, why “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” should remain invariant and what observation would expose asserting only that the callback ran.
Reasoned route. Begin with “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” Apply this procedure: Use a custom iterator with logged next and return. For the homework result stream, introduce one controlled variation that exposes asserting only that the callback ran. A complete test can prove ordered callbacks and cleanup on abrupt completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 2: CCA event feed. Contrast “A good forEach test checks effects, order and cleanup” with a generator of event records, controlled logging and an early callback failure. Before touching a tool, state which part of the case corresponds to the input, why “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” should remain invariant and what observation would expose asserting only that the callback ran.
Reasoned route. Begin with “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” Apply this procedure: Use a custom iterator with logged next and return. For the CCA event feed, introduce one controlled variation that exposes asserting only that the callback ran. A complete test can prove ordered callbacks and cleanup on abrupt completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 3: science reading pipeline. Stress-test “A good forEach test checks effects, order and cleanup” with a bounded prefix of measurements and a one-pass consumer. Before touching a tool, state which part of the case corresponds to the input, why “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” should remain invariant and what observation would expose asserting only that the callback ran.
Reasoned route. Begin with “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” Apply this procedure: Use a custom iterator with logged next and return. For the science reading pipeline, introduce one controlled variation that exposes asserting only that the callback ran. A complete test can prove ordered callbacks and cleanup on abrupt completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Case 4: revision generator. Explain “A good forEach test checks effects, order and cleanup” with fresh iterators, deliberate side effects and an asynchronous save boundary. Before touching a tool, state which part of the case corresponds to the input, why “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” should remain invariant and what observation would expose asserting only that the callback ran.
Reasoned route. Begin with “Testing only the final count can miss wrong order or missing IteratorClose behaviour.” Apply this procedure: Use a custom iterator with logged next and return. For the revision generator, introduce one controlled variation that exposes asserting only that the callback ran. A complete test can prove ordered callbacks and cleanup on abrupt completion. A complete answer explains why the near-miss fails, names the evidence that distinguishes it from the corrected case and transfers the rule without relying on the original variable names.
Diagnostic route
- Model check: draw or list the exact values, objects, paths, characters or repository states before the operation.
- Boundary check: construct the smallest safe input that reveals asserting only that the callback ran.
- Evidence check: distinguish a displayed result from ownership, caching, consumption, font behaviour or object type.
- Repair check: Use a custom iterator with logged next and return.
- Transfer check: repeat the rule in a second context and name what remains unchanged.
A parent does not need to memorise JavaScript Iterator.prototype.forEach to support “A good forEach test checks effects, order and cleanup”. Useful prompts are: “What did you expect?”, “Which state changed first?”, “What evidence tests Testing only the final count can miss wrong order or missing IteratorClose behaviour.?”, and “Can you make a smaller example of asserting only that the callback ran?” These questions keep ownership with the learner while making the reasoning visible.
Practice with an explained answer
Task. Design ordinary and throwing tests. Write the prediction before running anything. Include one ordinary case and one boundary case, then explain the difference in two causal sentences.
Answer route. The ordinary case ends at done; the throwing case includes return/cleanup and the propagated error. The final output alone earns only partial credit. A strong answer connects it to “Testing only the final count can miss wrong order or missing IteratorClose behaviour.”, shows the relevant intermediate evidence and states why asserting only that the callback ran would lead to a different conclusion.
Decision and transfer
Test the lifecycle, not just surface output. Prefer the choice whose semantics and failure modes are easiest to explain for the actual job. Shorter syntax is useful only when it preserves that clarity.
Mastery of “A good forEach test checks effects, order and cleanup” means the learner can predict the ordinary case, recognise asserting only that the callback ran, diagnose the earliest mistaken assumption, recover with “Use a custom iterator with logged next and return.” and defend this decision: Test the lifecycle, not just surface output. Once that causal chain is stable, continue to the next chapter rather than adding repetitions of the same surface exercise.
Previous section · Contents · Next section
Parent guide: choose the next useful step
For JavaScript Iterator.prototype.forEach, start with evidence rather than a label such as careless. Ask for one prediction and one trace. If the first transition is wrong, rebuild the model. If the model is sound but reference use is weak, practise finding the governing contract. If routine cases work but boundaries fail, vary one condition. If explanations transfer, move to a small project.
Punggol family routines are often busy, so use one complete JavaScript Iterator.prototype.forEach prediction–observation–explanation cycle while attention is good. Ten to twenty focused minutes can be enough. Keep a four-line record: concept, prediction, observed difference and next test. Stop when fatigue replaces reasoning.
Use disposable fixtures, temporary directories and test repositories. Backups remain necessary. The activities in this guide are proposed home-learning exercises, not advertised lesson times, branch facilities, school affiliations or outcome guarantees.
Capstone practice with explained routes
1. Prove eager consumption
Count source pulls during forEach. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: Iterator.prototype.forEach repeatedly pulls values until the iterator is done and does not return a helper. Apply: Count pulls before, during and after the call. Verify: The three values are processed immediately and the call completes with undefined. Then connect the result to exhaustion in Chapter 10. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
2. Trace callback arguments
Record values, zero-based indices and argument count. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: The procedure is called with the current value and a zero-based counter. Apply: Log arguments.length and the first two values. Verify: The output pairs 0 with a and 1 with b. Then connect the result to ignored returns in Chapter 7. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
3. Observe abrupt cleanup
Use a generator finally block and a throwing callback. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: If the procedure throws, the abrupt completion is propagated with IteratorClose. Apply: Use try/finally and catch outside for observation. Verify: The error propagates and generator cleanup runs. Then connect the result to source errors in Chapter 9. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
4. Bound an infinite source
Use take before forEach without hanging. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: Because it pulls until done, forEach never returns on a genuinely infinite iterator unless the callback throws. Apply: Apply take(n) before forEach in the safe fixture. Verify: An unbounded source needs a bound before the terminal consumer. Then connect the result to pipeline order in Chapter 14. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
5. Design retry-safe effects
Record partial progress and idempotent keys. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: The method is clearest when every consumed value should trigger an effect such as logging to a test sink. Apply: Use in-memory fixtures before external effects. Verify: The sink receives one record per value in order. Then connect the result to external-state invariants in Chapter 17. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
6. Rewrite async work
Replace an async callback with awaited for-of. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: A synchronous iterator forEach does not await promises returned by its callback. Apply: Use an explicit for-of loop with await inside an async function. Verify: The method returns undefined after starting callbacks according to synchronous iteration; promise completion needs separate coordination. Then connect the result to statement-level control in Chapter 20. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
7. Set a runtime policy
Record support and an approved fallback decision. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: forEach performs effects, map produces lazy transformed values, reduce produces one accumulated result and for-of offers statement-level control. Apply: State effect, output and early-exit needs first. Verify: The right construct follows desired output, laziness and control flow. Then connect the result to receiver rules in Chapter 2. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
8. Choose the consumer
Defend forEach, map, reduce or for-of for four tasks. Include an ordinary case, a boundary, a deliberate failure and a recovery. Write the expected state before each operation.
Explained route. Start with: Testing only the final count can miss wrong order or missing IteratorClose behaviour. Apply: Use a custom iterator with logged next and return. Verify: A complete test can prove ordered callbacks and cleanup on abrupt completion. Then connect the result to the terminal model in Chapter 1. A complete solution contains the input model, a trace, observed evidence, a correction and one transfer statement.
Frequently asked questions
How long should a practice session be?
Use one complete JavaScript Iterator.prototype.forEach prediction–observation–explanation cycle while attention remains good. Ten to twenty focused minutes can be enough.
Should every option or method be memorised?
No. For JavaScript Iterator.prototype.forEach, memorise governing distinctions and practise retrieving the primary reference. Understanding means predicting and explaining, not reciting a parameter list.
What if the result is correct but the explanation is weak?
Treat it as partial success. Ask for a trace and change one boundary. A reliable model survives controlled variation.
Is the shortest solution the best?
Not automatically. Prefer the solution whose semantics, failure modes and maintenance cost are easiest to justify for the actual project.
When should the official reference be used?
Use it whenever JavaScript Iterator.prototype.forEach syntax, supported values, version behaviour or implementation boundaries matter. The primary source settles the current contract.
How can a parent help without technical expertise?
Ask what was predicted, where the first difference appeared, what evidence matters and which smaller example could isolate it.
How do we test transfer?
Change the context, vocabulary and one boundary. Require the learner to identify the invariant before using a tool.
What should be saved after practice?
Keep the corrected rule, one trace, one boundary case and the next question. Avoid storing pages of unexplained output.
Can these exercises replace backups?
No. Use disposable examples and proper backups. Learning should not endanger schoolwork, repositories or personal data.
What counts as mastery?
The learner can predict, verify, diagnose, recover and justify a choice across more than one context, while knowing when to consult the current reference.

