In fact, the syntax yield * gen used to transfer control to another generator.
This is what MDN says about it:
The result of this expression is iterates over the operand.
Here is an example of how this works:
let seqPos = function * (max) { for (let i = 1; i <= max; i++) { yield i; } } let seqNeg = function * (min) { let start = min > 0 ? -1*min : min; for (let i = start; i < 0; i++) { yield i; } } let seq = function * (val) { yield * seqNeg(-1*val); yield 0; yield * seqPos(val); } for (let i of seq(2)) { console.log(i); }
The example above will output:
-2
-one
0
one
2
And here is the JSFiddle with an example.
As for the main issue, the difference between the structures:
for (let val of gen) { yield val; }
and
yield * gen;
is that in the first case, only the values of the child generator are returned, and in the second, control is completely transferred to the child generator.
This means that the call to the Generator.prototype.next , Generator.prototype.throw and Generator.prototype.return methods by the client code on the parent generator will be addressed to the child generator:
let innerGen = function * () { try { for (let i = 0; i < 3; i++) { console.log(yield i); } } catch (e) { console.log('Got you!'); } } let outerGen = function * () { let child = innerGen(); yield * child; } let g = outerGen(); g.next(); g.next("foo"); // выведет в консоль "foo" g.throw(new Error('Oops!')); // выведет "Got you!"
The situation with forwarding exceptions to the child iterator described by the participant @Roman Paradeev in the next answer is just a special case of the transfer of control to the child generator.