ES6-Generator(生成器)

概念

  1. generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次
  2. ES6提供的解决异步编程的方案之一
  3. Generator函数是一个状态机, 内部封装了不同状态的数据
  4. 用来生成遍历器对象
  5. 可暂停函数(惰性求值), yield可暂停, next方法可启动。 每次返回的是yield后的表达式结果

特点

  1. function 与函数名之间有一个星号
  2. 内部用yield表达式来定义不同的状态

generator基本语法:

{
  // genertaor基本定义
  let tell = function* (){
    yield 'a';
    yield 'b';
    return 'c'
  };

  let k = tell();

  console.log(k.next()); //{value: "a", done: false}
  console.log(k.next()); //{value: "b", done: false}
  console.log(k.next()); //{value: "c", done: true}
  console.log(k.next()); //{value: undefined, done: true}
}

next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。
当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。

方法:
Generator.prototype.next()
返回一个由 yield表达式生成的值。

Generator.prototype.return()
返回给定的值并结束生成器。

Generator.prototype.throw()
向生成器抛出一个错误。

generator和函数的对比:
我们先复习函数的概念。一个函数是一段完整的代码,调用一个函数就是传入参数,然后返回结果:

function foo(x) {
    return x + x;
}

var r = foo(1); // 调用foo函数

函数在执行过程中,如果没有遇到return语句(函数末尾如果没有return,就是隐含的return undefined;),控制权无法交回被调用的代码。

generator跟函数很像,定义如下

function* foo(x) {
    yield x + 1;
    yield x + 2;
    return x + 3;
}

generator和函数不同的是,generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次。

示例
一个无限迭代器

function* idMaker(){
    let index = 0;
    while(true) //无限迭代
        yield index++;
}

let gen = idMaker(); // "Generator { }"

console.log(gen.next().value); 
// 0
console.log(gen.next().value); 
// 1
console.log(gen.next().value); 
// 2
// ...

generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个好处要等到后面学了AJAX以后才能体会到。
没有generator之前的黑暗时代,用AJAX时需要这么写代码:

ajax('http://url-1', data1, function (err, result) {
    if (err) {
        return handle(err);
    }
    ajax('http://url-2', data2, function (err, result) {
        if (err) {
            return handle(err);
        }
        ajax('http://url-3', data3, function (err, result) {
            if (err) {
                return handle(err);
            }
            return success(result);
        });
    });
});

回调越多,代码越难看。

有了generator的美好时代,用AJAX时可以这么写:

try {
    r1 = yield ajax('http://url-1', data1);
    r2 = yield ajax('http://url-2', data2);
    r3 = yield ajax('http://url-3', data3);
    success(r3);
}
catch (err) {
    handle(err);
}

看上去是同步的代码,实际执行是异步的。

状态机事例

{
  //状态机事例
  let state = function* (){
    while(1){ //无限循环
      yield 'A';
      yield 'B';
      yield 'C';
    }
  }
  let status = state();
  console.log(status.next()); //{value: "A", done: false}
  console.log(status.next()); //{value: "B", done: false}
  console.log(status.next()); //{value: "C", done: false}
  console.log(status.next()); //{value: "A", done: false}
  console.log(status.next()); //{value: "B", done: false}
}

抽奖实例

//HTML部分代码省略

{
  //抽奖实例
  let draw = function(count){
    //具体抽奖逻辑
    console.log(`剩余${count}次`);
  }
  //Generator应用
  let residue = function* (count){
    while(count>0){
      count--;
      yield draw(count);//执行具体抽奖逻辑
    }
  }

  let star = residue(5);
  let btn = document.createElement('button');
  btn.id = 'start';
  btn.textContent = '抽奖';
  document.body.appendChild(btn);
  document.getElementById('start').addEventListener('click',function(){
    star.next();
  },false)
  
}

长轮询实例

{
  //长轮询
  let ajax = function* (){
    yield new Promise((resolve,reject) => {
      //模拟异步过程
      setTimeout(function () {
        resolve({code:0});
      },200);
    })
  }

  let pull = function(){
    let genertaor = ajax();
    let step = genertaor.next(); //step是一个Promise对象
    step.value.then(function(d){
      if(d.code != 0){
        setTimeout(function(){
          console.info('wait');
          pull();
        },1000);
      }else{
        console.info(d)
      }
    })
  }
  pull();
}

参考链接:
Generator - JavaScript | MDN
generator - 廖雪峰的官方网站
es6(十) genertaor - zh__quan的博客 - CSDN博客

猜你喜欢

转载自blog.csdn.net/weixin_43931898/article/details/102797967