Generator

Generator 是用 function* 和 yield 编写的可暂停函数,常用来生成迭代器。

#type / concept #status / growing #resource / javascript #resource / ecmascript

[!info] related notes

Generator

一句话定义

Generator 是一种能暂停和恢复的函数,调用后返回迭代器对象,通过 yield 逐步产出值,也能通过 next() 传入数据。

核心机制

基本写法与执行流程

function* gen() {
  console.log('start')
  yield 1
  console.log('middle')
  yield 2
  console.log('end')
}

const it = gen()       // 不执行函数体
it.next()              // "start" → { value: 1, done: false }
it.next()              // "middle" → { value: 2, done: false }
it.next()              // "end" → { value: undefined, done: true }

调用 gen() 不会执行函数体,而是返回一个迭代器。每次 next() 执行到下一个 yield 暂停。

yield 双向通信

yield 不仅能产出值,还能接收外部传入的值:

function* dialogue() {
  const name = yield '你叫什么名字?'
  const age = yield `${name},你多大了?`
  return `${name} 今年 ${age} 岁`
}

const it = dialogue()
it.next()              // { value: '你叫什么名字?', done: false }
it.next('小明')         // { value: '小明,你多大了?', done: false }
it.next(25)            // { value: '小明 今年 25 岁', done: true }

第一次 next() 不传值(因为没有 yield 等着接收),之后每次 next(arg)arg 会成为上一个 yield 表达式的返回值。

yield* 委托

yield* 把迭代委托给另一个可迭代对象:

function* concat(a, b) {
  yield* a
  yield* b
}

const result = [...concat([1, 2], [3, 4])]
// [1, 2, 3, 4]

yield* 也可以委托给另一个 Generator,实现递归:

function* flatten(arr) {
  for (const item of arr) {
    if (Array.isArray(item)) {
      yield* flatten(item)
    } else {
      yield item
    }
  }
}

[...flatten([1, [2, [3, 4]], 5])]  // [1, 2, 3, 4, 5]

return() 和 throw()

function* gen() {
  try {
    yield 1
    yield 2
  } finally {
    console.log('cleanup')
  }
}

const it = gen()
it.next()           // { value: 1, done: false }
it.return('done')   // "cleanup" → { value: 'done', done: true }

return() 提前结束 Generator 并执行 finallythrow() 在暂停处抛出异常。

Async Generator

async function* 结合了 Generator 的暂停能力和 Promise 的异步能力:

async function* fetchPages(urls) {
  for (const url of urls) {
    const res = await fetch(url)
    yield await res.json()
  }
}

// 使用 for await...of 消费
for await (const data of fetchPages(['/api/a', '/api/b'])) {
  console.log(data)
}

Async Generator 的 next() 返回 { value, done } 的 Promise,适合流式数据、分页请求等场景。

实际用途

1. 惰性序列生成

function* fibonacci() {
  let [a, b] = [0, 1]
  while (true) {
    yield a
    ;[a, b] = [b, a + b]
  }
}

const fib = fibonacci()
fib.next().value  // 0
fib.next().value  // 1
fib.next().value  // 1
fib.next().value  // 2

2. 状态机

function* trafficLight() {
  while (true) {
    yield '🔴 红灯'
    yield '🟢 绿灯'
    yield '🟡 黄灯'
  }
}

const light = trafficLight()
light.next().value  // "🔴 红灯"
light.next().value  // "🟢 绿灯"

3. 自定义可迭代对象

const range = {
  from: 1,
  to: 5,
  *[Symbol.iterator]() {
    for (let i = this.from; i <= this.to; i++) {
      yield i
    }
  }
}

[...range]  // [1, 2, 3, 4, 5]

边界与常见误解

  • Generator 不是 Promise,普通 Generator 是同步协议。yield 暂停的是函数执行,不是异步等待。
  • yield* 是委托,不是并发。它按顺序消费可迭代对象。
  • Generator 是单向的:调用方和 Generator 通过 next() / yield 交替执行,但不能同时运行。
  • Generator 返回的迭代器是状态不可逆的:走过的 yield 不能回退。
  • 一个 Generator 迭代器只能被一个消费者使用,不能多处共享同一个迭代器的状态。
  • 在现代代码中,很多 Generator 的场景已被 async/await 和异步迭代器替代,但 Generator 在惰性求值、状态机、自定义迭代协议中仍有独特价值。
创建于 2026/4/7 更新于 2026/5/27