Generator函数介绍

Generator函数

基本概念

英文意思为 “生成器”。

  • generator函数是es6提供的一种异步编程解决方案,语法行为与传统函数完全不同。从状态上,首先我们把他理解成一种状态机,封装了多个内部状态。另外执行generator函数胡返回一个遍历对象,就是说,generator函数还是一个遍历器对象生成函数,可以一次遍历函数中的每一个状态。

  • generator函数的两个特征

  1. function命令与函数名之间有一个星号;
  2. 函数体内部使用yield语句定义不同的内部状态(yield为“产出”的意思)。

调用generator函数后,该函数并不执行,返回的也不是函数的运行结果,而是一个指向内部状态的指针对象,也就相当于遍历器对象。下一步必须调用遍历器对象的next方法,使得指针移向下一个状态。就是说每次调用next方法,内部指针就从函数头部或上次停留的地方开始执行,直到遇到吓一跳yield语句或是return语句时停止。yield语句是generator函数的暂停执行,next是恢复执行。

yield语句

是generator函数的暂停标志。

  • next方法的运行逻辑:
  1. 遇到yield语句就暂停执行后面的操作,并将紧跟在yield后面表达式的值作为返回的对象的value属性值。
  2. 下次调用next方法时再继续往下执行,直到遇到下一个yield语句;
  3. 如果没有遇到新的yield语句,就一直运行到函数结束,直到遇到return语句为止,并将return语句后面的表达式的值作为返回对象的value属性值;
  4. 如果该函数没有return语句,则返回对象的value属性值为undefined

注意: yield语句为javascript提供了手动的 “惰性求值”
yield语句与return语句有相似之处也有区别,相似之处在于都返回紧跟在后面的值,区别在于return语句不具备记忆功能。

var arr = [1, [[2,3],4],[5,6]];
var flat = function* (a){
    a.forEach(function(item){
        if(typeof item !== 'number'){
            yield* flat(item);
        }else{
            yield item;
        }
    });
}

for(var f of flat(arr)){
    console.log(f);
}

这个例子将会报错,因为forEach方法的参数是个普通的函数,但是里面使用了yield语句,普通函数里面是不允许是使用yield语句的。
那么我们来修改一下

var arr = [1, [[2,3],4],[5,6]];
var flat = function* (a){
    var length = a.length;
    for(var i =0; i < length; i++){
        var item = a[i];
        if(typeof item !== 'number'){
            yield* flat(item);
        }else{
            yield item;
        }
    }  
};

for(var f of flat(arr)){
    console.log(f);
}// 1, 2, 3, 4, 5, 6
  • 与Iterator接口的关系

任意一个对象的Symbol.iterator方法等于该对象的遍历器对象生成函数,调用该函数会返回该对象的一个遍历器对象。
遍历器对象本身也有Symbol.iterator方法,执行后返回自身。

function* gen(){
    //some code
}
var g = gen();
console.log(g[Symbol.iterator]() === g);

gen是一个generator函数,调用它会生成一个遍历器对象g,他的Symbol.iterator属性也是一个遍历器对象生成函数,执行后返回自己。

yield* 语句

如果在generator函数内部调用另一个generator函数,默认情况下是没有效果的。

  • yield*语句,专门用来在一个generator函数里面执行另一个generator函数。

      function* foo(){
          yield 'a';
          yield 'b';
      }
      function* bar(){
          yield 'x';
          yield* foo();
          yield 'y';
      }
      
      for(let v of bar()){
          console.log(v);
      }
    

上面将会返回x, a, b, y。如果直接调用foo函数的话,将不会有什么效果。

  • 如果yield命令后面没有星号,将返回一个遍历器对象,加上星号后将会返回遍历对象的具体值。yield* 语句相当于在generator函数内部部署了一个for...of循环。

      function* concat(iter1, iter2){
          yield* iter1;
          yield* iter2;
      }
      //等同于
      function* concat(iter1, iter2){
          for(var value of iter1){
              yield value;
          }
           for(var value of iter2){
              yield value;
          }
      }
    

还应注意的是,如果在遍历数组时,用yield命令将会返回全部都的数组成员,yield* 则返回单个字符:即数组的第一个成员。

  • 如果generator函数有return语句,那么将会返回相应的数据,如果return()不带任何参数,则相应位置返回的值为undefined。

  • yield* 命令可以很方便的取出嵌套数组的所有成员。

      function* iterTree(tree){
          if(Array.isArray(tree)){
              for(let i = 0; i < tree.length; i++){
                  yield* iterTree(tree[i]);
              }
          }else{
              yield tree;
          }
      }
      
      const tree = ['a', ['b', 'c'], ['d', 'e']];
      
      for(let x of iterTree(tree)){
          console.log(x);
      }// a b c d e
    

Generator函数的this

Generator函数总是返回一个遍历器,这个遍历器是generator函数的实例。

function* g(){
    this.a = 11;
}

let obj = g();
console.log(obj.a);//undefined

上面的代码中,函数g总是返回遍历器对象,而不是this对象,这时使用new命令就不能生成对应函数的实例,因为函数返回的是一个内部指针。如果想让generator函数能有正常的构造函数,就可以采用:先生成一个空对象,使用bind方法绑定generator函数内部的指针,这样,构造函数调用之后这个空对象就是generator函数的实例对象了。

function* F(){
    yield this.x = 2;
    yield this.y = 3;
}
var obj = {};
var f = F.bind(obj)();

f.next();
f.next();
f.next();
console.log(obj);//Object{value: 2, done: false}

通过generator函数部署Ajax操作

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);
    });
}

var it = main();
it.next();

上面的main函数就是通过AJAX获取到的数据,makeAjaxCall函数的next方法中必须加上response参数,因为yield语句构成的表达式本身是没有值的,总是等于undefined。

posted @ 2017-04-26 21:31  美美王子  阅读(478)  评论(0编辑  收藏  举报