Skip to content

Generator 基础

Generator(生成器)是 ES6 引入的一种特殊函数,可以暂停执行并在稍后恢复。

基本语法#

定义生成器函数#

使用 function* 定义生成器函数:

function* myGenerator() {
yield 1
yield 2
yield 3
}
// 调用生成器函数返回一个迭代器
const gen = myGenerator()
gen.next() // { value: 1, done: false }
gen.next() // { value: 2, done: false }
gen.next() // { value: 3, done: false }
gen.next() // { value: undefined, done: true }

yield 表达式#

yield 暂停执行并返回值:

function* greet() {
console.log('开始')
yield '你好'
console.log('继续')
yield '世界'
console.log('结束')
return '完成'
}
const gen = greet()
gen.next()
// 输出: 开始
// 返回: { value: '你好', done: false }
gen.next()
// 输出: 继续
// 返回: { value: '世界', done: false }
gen.next()
// 输出: 结束
// 返回: { value: '完成', done: true }

作为迭代器#

生成器自动实现了迭代器协议:

function* numbers() {
yield 1
yield 2
yield 3
}
// for...of 遍历
for (const n of numbers()) {
console.log(n) // 1, 2, 3
}
// 扩展运算符
;[...numbers()] // [1, 2, 3]
// Array.from
Array.from(numbers()) // [1, 2, 3]

next() 的参数#

next() 的参数会成为上一个 yield 表达式的返回值:

function* conversation() {
const name = yield '你叫什么名字?'
const age = yield `${name},你多大了?`
return `${name}今年${age}`
}
const gen = conversation()
gen.next() // { value: '你叫什么名字?', done: false }
gen.next('张三') // { value: '张三,你多大了?', done: false }
gen.next(25) // { value: '张三今年25岁', done: true }

🤔 注意:第一次调用 next() 的参数会被忽略,因为此时没有等待的 yield

实际应用:累加器#

function* accumulator() {
let total = 0
while (true) {
const value = yield total
total += value ?? 0
}
}
const acc = accumulator()
acc.next() // { value: 0, done: false }
acc.next(10) // { value: 10, done: false }
acc.next(20) // { value: 30, done: false }
acc.next(5) // { value: 35, done: false }

return() 和 throw()#

return() 方法#

提前结束生成器:

function* gen() {
yield 1
yield 2
yield 3
}
const g = gen()
g.next() // { value: 1, done: false }
g.return('提前结束') // { value: '提前结束', done: true }
g.next() // { value: undefined, done: true }

throw() 方法#

在生成器内部抛出错误:

function* gen() {
try {
yield 1
yield 2
} catch (e) {
console.log('捕获错误:', e)
}
yield 3
}
const g = gen()
g.next() // { value: 1, done: false }
g.throw(new Error('出错了')) // 捕获错误: Error: 出错了
// { value: 3, done: false }

yield* 表达式#

yield* 用于委托给另一个生成器或可迭代对象:

function* inner() {
yield 'a'
yield 'b'
}
function* outer() {
yield 1
yield* inner() // 委托给 inner
yield 2
}
;[...outer()] // [1, 'a', 'b', 2]

遍历嵌套数组#

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]

遍历树结构#

function* traverseTree(node) {
yield node.value
if (node.children) {
for (const child of node.children) {
yield* traverseTree(child)
}
}
}
const tree = {
value: 1,
children: [
{ value: 2, children: [{ value: 4 }, { value: 5 }] },
{ value: 3, children: [{ value: 6 }] },
],
}
;[...traverseTree(tree)] // [1, 2, 4, 5, 3, 6]

实用案例#

无限序列#

// 自然数
function* naturals() {
let n = 1
while (true) {
yield n++
}
}
// 斐波那契数列
function* fibonacci() {
let [prev, curr] = [0, 1]
while (true) {
yield curr
;[prev, curr] = [curr, prev + curr]
}
}
// 取前 n 个
function take(generator, n) {
const result = []
for (const value of generator) {
if (result.length >= n) break
result.push(value)
}
return result
}
take(fibonacci(), 10) // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

范围生成器#

function* range(start, end, step = 1) {
if (step > 0) {
for (let i = start; i <= end; i += step) {
yield i
}
} else {
for (let i = start; i >= end; i += step) {
yield i
}
}
}
;[...range(1, 5)] // [1, 2, 3, 4, 5]
;[...range(0, 10, 2)] // [0, 2, 4, 6, 8, 10]
;[...range(5, 1, -1)] // [5, 4, 3, 2, 1]

唯一 ID 生成器#

function* idGenerator(prefix = '') {
let id = 1
while (true) {
yield `${prefix}${id++}`
}
}
const userIdGen = idGenerator('user_')
userIdGen.next().value // 'user_1'
userIdGen.next().value // 'user_2'
const orderIdGen = idGenerator('order_')
orderIdGen.next().value // 'order_1'

分页数据加载#

function* paginate(fetchPage) {
let page = 1
let hasMore = true
while (hasMore) {
const data = yield fetchPage(page)
if (data && data.length > 0) {
page++
} else {
hasMore = false
}
}
}
// 使用示例
async function loadAllData() {
const gen = paginate(async (page) => {
const response = await fetch(`/api/items?page=${page}`)
return response.json()
})
const allItems = []
let result = gen.next()
while (!result.done) {
const data = await result.value
if (data.length === 0) {
result = gen.next([])
} else {
allItems.push(...data)
result = gen.next(data)
}
}
return allItems
}

状态机#

function* trafficLight() {
while (true) {
yield '红灯'
yield '绿灯'
yield '黄灯'
}
}
const light = trafficLight()
light.next().value // '红灯'
light.next().value // '绿灯'
light.next().value // '黄灯'
light.next().value // '红灯'

生成器方法#

在对象和类中定义生成器方法:

// 对象方法
const obj = {
*generator() {
yield 1
yield 2
},
}
// 类方法
class MyClass {
*[Symbol.iterator]() {
yield 1
yield 2
yield 3
}
}
;[...new MyClass()] // [1, 2, 3]

小结#

特性说明
function*定义生成器函数
yield暂停执行并返回值
yield*委托给另一个可迭代对象
next()恢复执行,可传参
return()提前结束生成器
throw()在生成器内抛出错误

生成器是实现迭代器的简洁方式,也是理解 async/await 的基础。