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.fromArray.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 的基础。