今天看啥  ›  专栏  ›  Hisheng

Javascript 之《如何自动运行生成器》

Hisheng  · 掘金  ·  · 2018-05-14 03:29

文章预览

Javascript 之《如何自动运行生成器》

在 es6 中,引入了一个新的特性:生成器( generator ),用于控制函数运行流程,可实现对函数运行的中断与恢复。

生成器基本概念

一个简单的生成器对象定义如下:

function* Gen(){
  const name = 'Hisheng';
  yield `hello, ${name}`;
  console.log('bye');
}

那么如何运行它呢?直接 Gen() 是不行的,我们必须为它实例化一个迭代器。 你只需要记住:生成器的实例化结果是一个迭代器,或者迭代器是由生成器实例化而来。

const g = Gen();
g.next().value; // hello, Hisheng
g.next(); // bye

通过依次调用 g.next() 来依次运行函数中断点。

深入

那问题来了,要运行生成器,每次都要实例化并且手动调用 g.next() 多麻烦啊,能不能实现自动运行生成器呢?答案是肯定的。

我们先定义一个可以无限次迭代的生成器,例如典型的斐波那契函数:

function* fibonacci(){
  let x = 0, y = 1, z = 0;
  yield 1;
  while(true && z < 200){
    z = x+y;
    yield z;
    x = y;
    y = z;
  }
}

因为仅仅是用于演示,我们在生成器内部限制了其调用次数,不让它可无限调用,尽管理论上定义一个无限运行的生成器是没有问题的。

同时,我写了一个可用于自动运行生成器的方法,主要是结合了强大的 Promise,具体实现见函数注释:

function runGenerator(gen){
  // 先判断是否是生成器对象
  if(typeof gen !== 'function'){
    throw new TypeError('Generator must be a function');
  }
  const g = gen();
  if(!g.next){
    throw new TypeError('function is not a Generator');
  }
  const next = g.next();

  return new Promise((resolve, reject) => {
    handler(next).then(resolve).catch(reject);
  });

  function handler(next){
    return new Promise((resolve, reject) => {
      try {
        if(next.done){
          resolve();
        }else {
          const nnext = g.next();
          // 这一句只是为了打印出值来验证生成器是否有被运行,实际中可去掉
          console.log(nnext.value);
          // 递归调用 handler
          handler(nnext).then(resolve).catch(reject);
        }
      }catch (err){ // catch 生成器中可能抛出的异常
        reject(err);
      }
    });
  }
}

运行下看看

runGenerator(fibonacci); // 结果打印出了 1, 1, 2, 3, 5, 8...233

ok,至此已经达到我们想要的效果了。

题外

关于生成器的使用,最出名的莫过于 大神 TJ 专门写的库:co,实现更加巧妙。

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览