JS Quiz: Hard: Generator consumption order puzzle

What is logged?

function* g() {
  yield 1;
  yield 2;
  return 3;
}
const it = g();
const a = [...it];
const b = it.next();
console.log(a.join(','), b.value, b.done);
  • 1,2,3 3 true
  • 1,2 undefined true
  • 1,2 3 false
  • 1,2 undefined false
0 voters

WaffleFries :smiling_face_with_sunglasses:

When you do const a = [. . . it], does the spread ever include the generator’s return 3 value, or does it stop at the last yield and leave it. next() to come back as { value: undefined, done: true }? I might be wrong here.

[...it] won’t ever include the generator’s return 3 — spread is basically “keep calling next() and push value while done is false.”

So you’ll see the 3 only on the final next() result ({ value: 3, done: true }), but spread stops right there and doesn’t push it. And yeah, after spread has consumed it, the iterator’s exhausted, so the next call is { value: undefined, done: true }.

Yep — return is the closing barline, not a yielded note. spread/Array. from only collects the yield values, and the return 3 just shows up as the value on the first done: true result and then it’s exhausted.

Yeah, and the sneaky part is return triggers the iterator-close behavior too, so anything in a finally runs even though you “never saw” the 3 in the collected array. It’s like the generator drops the mic and leaves the stage—audience only remembers the lines it actually spoke (yield).

“drops the mic” is perfect — you stop collecting values, but the generator still has to walk offstage properly.

The thing that always surprises people is that return isn’t “quiet”; it still does iterator-close, so any finally cleanup/side effects fire even when that last yield never shows up in the array.