• Generator


    GeneratorES6中新增的语法,和 Promise 一样,都可以用来异步编程。Generator函数可以说是Iterator接口的具体实现方式。Generator 最大的特点就是可以控制函数的执行。

    • function* 用来声明一个函数是生成器函数,它比普通的函数声明多了一个*,*的位置比较随意可以挨着 function 关键字,也可以挨着函数名
    • yield 产出的意思,这个关键字只能出现在生成器函数体内,但是生成器中也可以没有yield 关键字,函数遇到 yield 的时候会暂停,并把 yield 后面的表达式结果抛出去
    • next作用是将代码的控制权交还给生成器函数
    function *foo(x) {
      let y = 2 * (yield (x + 1))
      let z = yield (y / 3)
      return (x + y + z)
    }
    let it = foo(5)
    console.log(it.next())   // => {value: 6, done: false}
    console.log(it.next(12)) // => {value: 8, done: false}
    console.log(it.next(13)) // => {value: 42, done: true}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面这个示例就是一个Generator函数,我们来分析其执行过程:

    • 首先 Generator 函数调用时它会返回一个迭代器
    • 当执行第一次 next 时,传参会被忽略,并且函数暂停在 yield (x + 1) 处,所以返回 5 + 1 = 6
    • 当执行第二次 next 时,传入的参数等于上一个 yield 的返回值,如果你不传参,yield 永远返回 undefined。此时 let y = 2 * 12,所以第二个 yield 等于 2 * 12 / 3 = 8
    • 当执行第三次 next 时,传入的参数会传递给 z,所以 z = 13, x = 5, y = 24,相加等于 42

    yield实际就是暂缓执行的标示,每执行一次next(),相当于指针移动到下一个yield位置

    总结一下Generator函数是ES6提供的一种异步编程解决方案。通过yield标识位和next()方法调用,实现函数的分段执行

    遍历器对象生成函数,最大的特点是可以交出函数的执行权

    • function 关键字与函数名之间有一个星号;
    • 函数体内部使用 yield表达式,定义不同的内部状态;
    • next指针移向下一个状态

    这里你可以说说 Generator的异步编程,以及它的语法糖 asyncawiat,传统的异步编程。ES6 之前,异步编程大致如下

    • 回调函数
    • 事件监听
    • 发布/订阅

    传统异步编程方案之一:协程,多个线程互相协作,完成异步任务。

    // 使用 * 表示这是一个 Generator 函数
    // 内部可以通过 yield 暂停代码
    // 通过调用 next 恢复执行
    function* test() {
      let a = 1 + 2;
      yield 2;
      yield 3;
    }
    let b = test();
    console.log(b.next()); // >  { value: 2, done: false }
    console.log(b.next()); // >  { value: 3, done: false }
    console.log(b.next()); // >  { value: undefined, done: true }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    从以上代码可以发现,加上 *的函数执行后拥有了 next 函数,也就是说函数执行后返回了一个对象。每次调用 next 函数可以继续执行被暂停的代码。以下是 Generator 函数的简单实现

    // cb 也就是编译过的 test 函数
    function generator(cb) {
      return (function() {
        var object = {
          next: 0,
          stop: function() {}
        };
    
        return {
          next: function() {
            var ret = cb(object);
            if (ret === undefined) return { value: undefined, done: true };
            return {
              value: ret,
              done: false
            };
          }
        };
      })();
    }
    // 如果你使用 babel 编译后可以发现 test 函数变成了这样
    function test() {
      var a;
      return generator(function(_context) {
        while (1) {
          switch ((_context.prev = _context.next)) {
            // 可以发现通过 yield 将代码分割成几块
            // 每次执行 next 函数就执行一块代码
            // 并且表明下次需要执行哪块代码
            case 0:
              a = 1 + 2;
              _context.next = 4;
              return 2;
            case 4:
              _context.next = 6;
              return 3;
    		// 执行完毕
            case 6:
            case "end":
              return _context.stop();
          }
        }
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    C++基础——对于C语言缺点的补充(2)
    Vue 搭配 Spring MVC 创建一个 web 项目
    淘宝API接口大全
    OpenFeign服务调用与负载均衡
    终端常用脚本命令
    脱离CRUD苦海 !性能优化全栈小册来了!
    Google Chrome(谷歌浏览器)安装使用
    计算机操作系统:进程的同步和互斥
    02-csa练习题
    如何优雅的处理编码与文本之间转换工作?
  • 原文地址:https://blog.csdn.net/php_martin/article/details/125856774