A generator object (Generators) is both an iterator and an iterable. Inside a generator, yield* iterable forwards every value of another iterable and evaluates to that generator's return value, which spread and for...of discard. A generator that delegates to itself walks recursive structures such as DOM trees.
function* walk(node, depth = 0) { // recursion through yield*
yield '-'.repeat(depth) + node.v;
for (const kid of node.kids ?? []) yield* walk(kid, depth + 1);
}
const tree = { v: 'html', kids: [{ v: 'head' }, { v: 'body', kids: [{ v: 'p' }] }] };
console.log([...walk(tree)]);
function* inner() { yield* 'ok'; return 42; } // a string is iterable too
function* outer() { const result = yield* inner(); yield result; }
console.log([...inner()], [...outer()]);[ 'html', '-head', '-body', '--p' ] [ 'o', 'k' ] [ 'o', 'k', 42 ]
Each next() passes through every level of a yield* chain, so for very deep trees an explicit stack is faster.