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.

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.