Understanding yield* in JavaScript Generators
The yield* expression in JavaScript is used to delegate
iteration from one generator function to another iterable object, such
as another generator, an array, a string, or a custom iterable. Instead
of manually looping over an iterable and yielding each element
individually, yield* pauses the host generator and passes
control directly to the target iterable until it is exhausted. This
article explains the purpose of yield*, how it simplifies
generator composition, and how it manages two-way communication and
return values.
What is Generator Delegation?
Generator delegation allows a generator function to call another generator (or iterable) and yield all its values sequentially as if they were yielded directly by the outer generator.
Without yield*, you would have to manually iterate over
nested generators using a for...of loop:
function* subGenerator() {
yield 'A';
yield 'B';
}
// Without yield*
function* manualDelegation() {
for (const value of subGenerator()) {
yield value;
}
}With yield*, the delegation is concise and
declarative:
// With yield*
function* delegatedGenerator() {
yield* subGenerator();
}Primary Purposes and Capabilities of yield*
1. Delegating to Any Iterable
The yield* expression works with any valid JavaScript
iterable, not just other generators. It can flatten arrays, strings,
Sets, Maps, and custom iterable objects.
function* displayElements() {
yield* [1, 2, 3];
yield* 'Hi';
}
const iterator = displayElements();
// Yields: 1, 2, 3, 'H', 'i'2. Capturing Return Values
When a delegated generator finishes execution with a
return statement, that return value is evaluated as the
result of the yield* expression inside the outer generator.
Standard for...of loops ignore return values, but
yield* captures them.
function* compute() {
yield 1;
yield 2;
return 'Done with computation';
}
function* main() {
const result = yield* compute();
yield result; // Yields the return value
}
const gen = main();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 'Done with computation'3. Bidirectional Communication (Passing Values and Errors)
The yield* expression establishes a direct pipeline
between the caller and the delegated generator.
next(value): Arguments passed tonext()on the outer generator are passed directly into the activeyieldinside the delegated generator.throw(error): Errors thrown into the outer generator viagen.throw()are forwarded into the delegated generator, allowing it to catch the error locally with atry...catchblock.return(value): Callinggen.return()triggers the cleanup logic (such asfinallyblocks) inside the delegated generator before terminating the outer generator.
Summary
The yield* expression serves as the mechanism for
composing modular generator functions in JavaScript. It eliminates
boilerplate iteration code, enables recursive generator structures (such
as tree traversals), captures completion return values, and seamlessly
routes data, errors, and return calls directly to the delegated
iterable.