Learning Objectives

  • Understand yield* syntax
  • Learn to compose generators
  • Master delegation patterns
  • Build modular generator systems

What is Yield Delegation?

The yield* expression delegates to another generator or iterable, yielding all its values.

function* inner() {
  yield 1;
  yield 2;
}

function* outer() {
  yield 'start';
  yield* inner(); // Delegate to inner generator
  yield 'end';
}

console.log([...outer()]); // ['start', 1, 2, 'end']

Without vs With Delegation

// Without yield* - yields the generator object
function* withoutDelegation() {
  yield inner(); // Yields generator object
}

console.log([...withoutDelegation()]); 
// [Object [Generator] {}]

// With yield* - yields all values
function* withDelegation() {
  yield* inner(); // Yields 1, then 2
}

console.log([...withDelegation()]); // [1, 2]

Delegating to Arrays

function* arrayDelegation() {
  yield* [1, 2, 3];
  yield* 'hello';
  yield* new Set([4, 5, 6]);
}

console.log([...arrayDelegation()]); 
// [1, 2, 3, 'h', 'e', 'l', 'l', 'o', 4, 5, 6]

Composing Generators

function* numbers() {
  yield 1;
  yield 2;
  yield 3;
}

function* letters() {
  yield 'a';
  yield 'b';
  yield 'c';
}

function* combined() {
  yield* numbers();
  yield* letters();
}

console.log([...combined()]); // [1, 2, 3, 'a', 'b', 'c']

Recursive Generators

function* flatten(array) {
  for (const item of array) {
    if (Array.isArray(item)) {
      yield* flatten(item); // Recursive delegation
    } else {
      yield item;
    }
  }
}

const nested = [1, [2, [3, [4]], 5], 6];
console.log([...flatten(nested)]); // [1, 2, 3, 4, 5, 6]

Tree Traversal

class TreeNode {
  constructor(value, children = []) {
    this.value = value;
    this.children = children;
  }
  
  *traverse() {
    yield this.value;
    for (const child of this.children) {
      yield* child.traverse(); // Delegate to child
    }
  }
}

const tree = new TreeNode(1, [
  new TreeNode(2, [
    new TreeNode(4),
    new TreeNode(5)
  ]),
  new TreeNode(3, [
    new TreeNode(6)
  ])
]);

console.log([...tree.traverse()]); // [1, 2, 4, 5, 3, 6]

Return Values with Delegation

function* inner() {
  yield 1;
  yield 2;
  return 'inner done';
}

function* outer() {
  const result = yield* inner();
  console.log('Inner returned:', result);
  yield 3;
}

console.log([...outer()]);
// Logs: Inner returned: inner done
// Returns: [1, 2, 3]

Real-World: File System Walker

class Directory {
  constructor(name, items = []) {
    this.name = name;
    this.items = items;
  }
  
  *walk() {
    yield this.name;
    
    for (const item of this.items) {
      if (item instanceof Directory) {
        yield* item.walk(); // Recurse into subdirectories
      } else {
        yield item;
      }
    }
  }
}

const root = new Directory('root', [
  'file1.txt',
  new Directory('subdir1', [
    'file2.txt',
    'file3.txt'
  ]),
  new Directory('subdir2', [
    'file4.txt'
  ])
]);

for (const item of root.walk()) {
  console.log(item);
}
// root
// file1.txt
// subdir1
// file2.txt
// file3.txt
// subdir2
// file4.txt

Modular Pipeline

function* source() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

function* map(iterable, transform) {
  for (const item of iterable) {
    yield transform(item);
  }
}

function* pipeline() {
  yield* map(
    filter(source(), x => x % 2 === 0),
    x => x * 2
  );
}

console.log([...pipeline()]); // [4, 8]

Generator Composition Pattern

function* compose(...generators) {
  for (const gen of generators) {
    yield* gen;
  }
}

function* gen1() { yield 1; yield 2; }
function* gen2() { yield 3; yield 4; }
function* gen3() { yield 5; yield 6; }

const combined = compose(gen1(), gen2(), gen3());
console.log([...combined]); // [1, 2, 3, 4, 5, 6]

Key Takeaways

  • yield* delegates to another iterable
  • ✅ Enables generator composition
  • ✅ Perfect for recursive structures
  • ✅ Captures return values from delegated generators
  • ✅ Works with any iterable (arrays, strings, etc.)
  • ✅ Builds modular generator systems

Next Steps

Now let's explore two-way communication patterns in depth!