美文网首页
es6 Generator

es6 Generator

作者: wmtcore | 来源:发表于2016-08-12 16:10 被阅读0次

    简介

    • 把它理解成一个状态机,封装了多个内部状态,执行Generator函数会返回一个遍历器对象,可以依次遍历Generator函数内部的每一个状态。
    • 定义时function与函数名之间有一个星号
    • 内部使用yield(产出)语句,定义不同的内部状态
    • 调用遍历器对象的next方法,使指针移向下一个状态

    调用Generator函数后,该函数并不执行,而是返回一个指向内部状态的指针对象,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行

    function* helloWorldGenerator() {
      yield 'hello';
      yield 'world';
      return 'ending';
    }
    
    var hw = helloWorldGenerator();
    
    hw.next()
    // { value: 'hello', done: false }
    
    hw.next()
    // { value: 'world', done: false }
    
    hw.next()
    // { value: 'ending', done: true },执行完成done变成true
    
    hw.next()
    // { value: undefined, done: true }
    

    yield语句

    • 遍历器对象的next遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值
    • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句
    • 如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值,若无则返回undefined
    • yield语句如果用在一个表达式之中,必须放在圆括号里面,作函数参数或赋值表达式的右边除外

    yield与return区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield语句,所以Generator函数可以返回一系列的值,即生成器之意

    yield句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。第一次使用next方法时,不能带有参数,V8引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。

    function* f() {
      for(var i=0; true; i++) {
        var reset = yield i;
        if(reset) { i = -1; }
      }
    }
    
    var g = f();
    
    g.next() // { value: 0, done: false }
    g.next() // { value: 1, done: false }
    g.next(true) // { value: 0, done: false }
    
    

    通过next方法的参数,向Generator函数内部输入值

    function* dataConsumer() {
      console.log('Started');
      console.log(`1. ${yield}`);
      console.log(`2. ${yield}`);
      return 'result';
    }
    
    let genObj = dataConsumer();
    genObj.next();
    // Started
    genObj.next('a')
    // 1. a
    genObj.next('b')
    // 2. b
    

    与Iterator接口的关系

    • 执行Generator函数后返回Iterator对象

    for...of循环、扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是使用遍历器接口,即可以自动遍历Generator函数

    扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
    
    let z = { a: 3, b: 4 };
    let n = { ...z };
    n // { a: 3, b: 4 }
    
    扩展运算符可以用于合并两个对象。
    
    let ab = { ...a, ...b };
    // 等同于
    let ab = Object.assign({}, a, b);
    
    

    Generator.prototype.return()

    • 遍历器对象的return方法,可以返回给定的值,并且终结遍历Generator函数
    function* gen() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    var g = gen();
    
    g.next()        // { value: 1, done: false }
    g.return("foo") // { value: "foo", done: true },return不提供参数,则返回值的vaule属性为undefined
    g.next()        // { value: undefined, done: true }
    
    

    yield*语句

    • 用来在一个Generator函数里面执行另一个Generator函数
    function* foo() {
      yield 'a';
      yield 'b';
    }
    
    function* bar() {
      yield 'x';
      yield* foo();
      yield 'y';
    }
    
    // 等同于
    function* bar() {
      yield 'x';
      yield 'a';
      yield 'b';
      yield 'y';
    }
    
    // 等同于
    function* bar() {
      yield 'x';
      for (let v of foo()) {
        yield v;
      }
      yield 'y';
    }
    
    for (let v of bar()){
      console.log(v);
    }
    // "x"
    // "a"
    // "b"
    // "y"
    
    
    

    yield*语句等同于在Generator函数内部,部署一个for...of循环 ,如果yield*后面跟着一个数组,由于数组原生支持遍历器,因此就会遍历数组成员,还有字符串之类有Iterator接口的数据结构

    function* gen(){
      yield* ["a", "b", "c"];
    }
    
    gen().next() // { value:"a", done:false }
    
    
    // 下面是二叉树的构造函数,
    // 三个参数分别是左树、当前节点和右树
    function Tree(left, label, right) {
      this.left = left;
      this.label = label;
      this.right = right;
    }
    
    // 下面是中序(inorder)遍历函数。
    // 由于返回的是一个遍历器,所以要用generator函数。
    // 函数体内采用递归算法,所以左树和右树要用yield*遍历
    function* inorder(t) {
      if (t) {
        yield* inorder(t.left);
       �����        yield t.label;
        yield* inorder(t.right);
      }
    }
    
    // 下面生成二叉树
    function make(array) {
      // 判断是否为叶节点
      if (array.length == 1) return new Tree(null, array[0], null);
      return new Tree(make(array[0]), array[1], make(array[2]));
    }
    let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
    
    // 遍历二叉树
    var result = [];
    for (let node of inorder(tree)) {
      result.push(node);
    }
    
    result
    // ['a', 'b', 'c', 'd', 'e', 'f', 'g']
    
    

    Generator的一些应用

    异步转同步

    可以把异步操作写在yield语句里面,等到调用next方法时再往后执行

    function* main() {
        var result = yield request("http://some.url");
        var resp = JSON.parse(result);
        console.log(resp.value);
    }
    function request(url) {
        makeAjaxCall(url, function(response) {
            it.next(response);   //因为yield语句构成的表达式,本身是没有值的,必须加上response参数
        });
    }
    var it = main();
    it.next();
    
    

    流程控制

    多个任务需要并列执行时,可以采用数组的写法

    function* parallelDownloads() {
      let [text1,text2] = yield [
        taskA(),
        taskB()
      ];
      console.log(text1, text2);
    }
    
    

    部署iterator接口

    function* iterEntries(obj) {
      let keys = Object.keys(obj);
      for (let i=0; i < keys.length; i++) {
        let key = keys[i];
        yield [key, obj[key]];
      }
    }
    
    let myObj = { foo: 3, bar: 7 };
    
    for (let [key, value] of iterEntries(myObj)) {
      console.log(key, value);
    }
    
    // foo 3
    // bar 7
    
    

    相关文章

      网友评论

          本文标题: es6 Generator

          本文链接:https://www.haomeiwen.com/subject/fehesttx.html