ES6的新语法。多种角度理解:

  1. 从语法上,是一个状态机,封装了多个内部状态
  2. 从执行结果,返回一个遍历器对象,也是一个遍历器生成函数。这个返回的结果可以依次遍历内部每个状态。
  3. 从执行成面,是对“协程”的实现,可以多个线程(函数)交换控制权

    1 语法

    1.1 形式上是普通函数,但是带有两个特征(* 和 yield)

    generator函数可以不用yield语句,但是yield语句只能用在generator函数中。
    1. function* hello(){ // 函数命名带有*
    2. yield "world" // 函数体内使用yield语句 yield 翻译 产出
    3. yield "girl"
    4. return "nice"
    5. }
    函数有三个状态: world 、 girl 、return语句

    1.2 执行结果是一个指向内部状态的指针对象,既遍历器对象Iterator Object

    1. let ge = hello()

    1.3 调用遍历器对象的next方法,使指针移到下一个状态

    既从函数头部或上次停下的地方开始执行,直到遇到下一个yield语句或retrun语句为止。
    next方法返回一个对象,value属性为当前yield或return后面表达式的值,done属性为遍历器是否结束
    1. ge.nxet()
    2. // {value: "world", done: false}
    3. ge.nxet()
    4. // {value: "girl", done: false}
    5. ge.nxet()
    6. // {value: "nice", done: true}
    7. ge.nxet()
    8. // {value: undefined, done: true} //如果运行完毕,则value属性为undefined
    image.png

1.3.1 yield 语句和其他语句一起使用,

理解next执行顺序以及yield暂停位置
同时与其他表达式一起,需要用括号包住
image.png

1.3.2 只有yield的执行结果

执行完yield 2后,并不知道已经执行完,调用还会继续执行,才返回done:true
image.png

1.4 next参数

yield表达式本身没有返回值,或者说总是返回undefined,
next方法可以带一个参数,作为上一条yield语句的返回值

image.png

1.5 generator生成的遍历器对象可以被for…of自动遍历,无需调用next

  1. function* foo() {
  2. yield 1
  3. yield 2
  4. yield 3
  5. yield 4
  6. return 5
  7. }
  8. for(let v of foo()){
  9. console.log(v)
  10. }

image.png

1.6 Generator.prototype.throw()

参考
可在函数体外抛出错误,然后再generator函数体内捕获

  1. var a = function*() {
  2. try {
  3. yield
  4. }catch(e){
  5. console.log("内部捕获" + e)
  6. }
  7. }
  8. var i = a()
  9. a.throw("a")
  10. // 内部捕获a

2 在异步操作中的应用

generator函数可以暂停执行和恢复执行,这是它能封装异步任务的更本原因。
函数体内外数据交换 + 错误处理机制 使其成为异步编程的完整解决方案。

2.1 手动控制流程

  1. var fetch = require('node-fetch')
  2. //封装异步操作,完全是同步写法
  3. function* gen() {
  4. var url = "http://api.xxxxxx"
  5. var result = yield fetch(url)
  6. consoel.log(result)
  7. }
  8. // 执行
  9. var g = gen()
  10. // 启动异步操作
  11. var result = g.next()
  12. result.value
  13. .then((data){
  14. return data.json()
  15. }).then(function(data){
  16. // 异步操作成成功后继续向下执行,并注入异步操作结果
  17. g.next(data)
  18. })

优点: generator函数将异步操作写成同步写法,很简洁
缺点:流程管理不方便,需要手动判断何时执行第一阶段,以及后续阶段

2.2 Thunk自动控制流程

这个时候就会用到Thunk函数(只有一个参数,且这个参数为callback形式)自动执行generator函数。

  1. //自动执行以及流程控制函数
  2. function run(fn){
  3. let gen = fn()
  4. //let result = gen.next()
  5. //if(result.done) return
  6. //result.value(function(err,data) {
  7. //let result = gen.next(data)
  8. //if(result.done) return
  9. //result.value()
  10. //})
  11. function next(err, date){
  12. let result = gen.next(data)
  13. if(result.done) return
  14. result.value(next)
  15. }
  16. next()
  17. }
  18. // generator函数封装的异步任务 唯一的要求是:yield后面每一个异步操作都是一个Thunk函数
  19. function* fn(){
  20. let f1 = yield readFile("fileA")
  21. let f1 = yield readFile("fileA")
  22. let f1 = yield readFile("fileA")
  23. }
  24. // 执行
  25. run(fn)

2.3 Promise自动控制

Thunk函数不是generator函数自动执行的唯一方案,关键有一种机制接受和交换程序的执行权
除了Thunk这种用回调函数,Promise对象也可以做到
唯一要求也是:yield后面是promise对象。

2.4 co模块是常用的自动执行及控制流程的通用函数工具。支持thunk和promise两种模式。

3 实际应用

在eggjs中controller中用到
image.png