Generator 简介
Generator 定义
Generator 是隐藏类 Iterator 的子类,Generator 对象是一个 JavaScript 的标准内置对象,它由生成器函数返回并且它符合可迭代协议和迭代器协议
生成器函数 形如 function* name() {},它内部可以包含 yield 表达式,具体功能下文再述
可迭代协议:简单说就是一个对象实现了 [Symbol.iterator]() 方法,这个方法无参,返回一个符合迭代器协议的对象
迭代器协议:定义了产生一系列值(无论是有限个还是无限个)的标准方式,当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。最简的迭代器对象必须实现 next() 方法,该方法无参或接受一个参数,返回一个符合 IteratorResult 接口的对象。IteratorResult 简单地描述一下就是:
1 | interface IteratorResult { |
其中 done 标识是否完成了迭代
写一个简单的符合可迭代协议和迭代器协议的对象就是:
1 | const myIteratorObj = { |
而普通的对象被数组重组时会抛错 TypeError: object is not iterable
Generator 生成器函数
回到 Generator,它有什么用?这就得继续讨论生成器函数
生成器函数中的 yield 表达式会使生成器函数返回的 Generator 对象执行 next() 方法后暂停执行生成器函数主体,由此可以用来异步编程。对于上文的 myIteratorObj,可以由生成器改写为:
1 | const myGenerator = function* () { |
其中的 yield 关键字:
- 只能出现在 Generator 函数中
- 只能用来暂停和回复生成器函数
因此 yield 不可能存在于 Array.prototype.forEach() 等方法中,即以下用法是语法错误的:
1 | const myGenerator = function* () { |
而应该为:
1 | const myGenerator = function* () { |
符合迭代器协议的对象的 next() 方法,也可以接受一个参数,该参数会作为上一个 yield 表达式的返回值传入,并覆盖上一个 yield 表达式的返回值,比如:
1 | function* generator() { |
上述代码第二次执行 next() 并传入 9,得到的 value 即为 11,而不是 3
应用场景
生成器函数的 MDN 文档中提到了生成器函数可以解决回调地狱问题,比如顺序读取文件:
1 | function readFileByCallback() { |
三层回调恐怖如斯,可以使用 Generator 改写为:
1 | function* readFileByGenerator() { |
但这种写法将 f 这个外部变量高耦合到 readFileByGenerator() 方法中,还是不够优雅,于是需要用到以下的 Thunk 函数
Thunk 函数
首先了解一下两种求值策略
- 传值调用:传入所需参数进行计算
- 传名调用:传入计算方法、参数等进行计算
Thunk 函数是传名调用的实现方式之一,可以实现自动执行 Generator 函数。示例:
1 | const fs = require("fs") |
假设上述代码中的 Thunk 中的两个匿名函数从外到里依次为 thunk1、thunk2,则整段代码的执行过程如下:

上图对应的 trace 的生成代码见 thunk.js
上图对应的代码见 thunk-trace
其中,同步任务在 run 方法执行后已经结束,后续的均为 readFile 或者 Generator 函数的其他异步的任务
需要注意的是,在调用 readFileThunk 后,返回的并不是读取文件的值,而是返回了 Thunk 中最内层的匿名函数 thunk2;接下来调用 result.value(next) 时,才真正执行读取文件的操作,并且读取文件后的内容传递给了 next(err, data) 中的 data,由于向 gen.next() 传递参数会被当作上一个 yield 表达式的返回值,因此 s1 变成了读取文件的内容 data 并被输出,其他 yield 同此
yield 暂停生成器执行机制的实现原理
一个线程中可以存在多个协程,但同时只能执行一个,Generator 函数是协程在 ES6 的实现。当遇到 yield 表达式,则挂起 x 协程,交给其他协程,next() 唤醒 x 协程
补充
yield*
类似 yield 的机制,执行到 yield* 时,把执行权交给紧跟的生成器。可以复用生成器
1 | function* generator1() { |
return(param) 和 throw(param)
实际的 Generator 对象还包含 return() 和 throw()
顾名思义,return() 会提前中止 Generator 的执行;而 throw() 会主动引起报错,如果生成器主体没有捕获改错误,则会继续上抛错误,不传入任何异常时捕获到的错误是一个 undefined
生成器中的 return
在生成器主体中提前调用 return,则会提前终止迭代,迭代结果的 done 会被置为 true




Mosu is located on the shore of Mosu Lake, facing the vast Chu Sea, backed by the Yihan Mountains. Thousands of miles of Mosu Desert can not erode the Mosu Valley. Thus the Mosu Empire was established.