Generator
Generator 是用 function* 和 yield 编写的可暂停函数,常用来生成迭代器。
#type / concept
#status / growing
#resource / javascript
#resource / ecmascript
[!info] related notes
- 所属 MOC: ES6 新特性 MOC, ECMAScript MOC
- 前置概念: Iterator
- 并列概念: Iterable
- 关系笔记: Iterable、Iterator、Generator 关系
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 并执行 finally。throw() 在暂停处抛出异常。
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 在惰性求值、状态机、自定义迭代协议中仍有独特价值。