Skip to content

2803. 生成器函数

2803. 阶乘生成器

js
function* factorial(n) {
    if (n === 0) return 1;

  let result = 1;
  for (let i = 1; i <= n; i++) {
    result *= i;
    yield result; // 每次产出 i! 的值
  }
}

yield 的暂停语义

Generator 函数最核心的能力是:在执行过程中暂停,保留所有局部状态,之后再从暂停点继续

调用 factorial(5) 不会执行函数体,只是创建一个 Generator 对象。.next() 才开始执行,执行到第一个 yield 时暂停并向外产出值。再次 .next() 从 yield 之后继续,直到下一个 yield 或函数结束。

这使得 Generator 更接近一个可控的状态机,推进时机由调用方决定,而不是函数自行一次性执行到底。

yield 双向通信

js
function* dialogue() {
  const answer1 = yield "你是谁?";
  const answer2 = yield "你从哪里来?";
  return `你好,${answer1},来自${answer2}`;
}

const gen = dialogue();

console.log(gen.next().value);         // "你是谁?"
console.log(gen.next("小明").value);   // "你从哪里来?"
console.log(gen.next("北京").value);   // "你好,小明,来自北京"

第一个 .next() 产出了 yield 右侧的值。第二个 .next("小明") 将 "小明" 作为上一个 yield 表达式的结果赋给 answer1,同时执行到下一个 yield 产出新值。

注意:第一个 .next() 如果传参会被忽略——因为此时还没有 yield 在等待接收值。Generator 总是需要一个 .next() 来启动,然后才进入「产出/接收」的循环。

yield* 委托

js
function* inner() {
  yield "A";
  yield "B";
}

function* outer() {
  yield "Start";
  yield* inner(); // 委托 inner 的产出
  yield "End";
}

yield* 将生成控制权委托给另一个 Generator。outer 的迭代器在 yield* 处暂停,inner 的迭代器接管。inner 结束后,outer 继续执行剩余代码。

这个机制的价值在于组合:可以把一个大的 Generator 拆成多个小段,再通过 yield* 串起来。它和普通函数调用类似,但每一步都保留了可暂停性。

实际场景

Generator 在 JavaScript 中并不是日常使用的特性,但在几个场景下非常自然:

  • 惰性序列:无限序列、大数据集的按需生成
  • 异步流程控制redux-saga 用它实现复杂的异步编排(每个 yield 等待一个 effect 完成)
  • 自定义迭代器:通过 Symbol.iterator + Generator 实现对象的可迭代协议