JavaScript 生成器Generator

94 阅读2分钟

Generator

Generator 对象由生成器函数返回并且它符合可迭代协议和迭代器协议。

Generator 生成器是 ES6 提供的一种异步编程解决方案。拥有在函数块中暂停和恢复代码执行的能力。

执行 Generator 生成器 会返回一个 ( Iterator )迭代器对象,也就是说:Generator 函数除了状态机,还是一个迭代器对象生成函数,返回的迭代器对象,可以依次遍历 Generator 函数内部的每一个状态。

function* foo() {
    yield 0;
    yield 1;
}
function* bar() {
    yield 'x';
    // 可以在Generator函数里面再套一个Generator
    yield* foo();
    yield 'y';
}
for (let v of bar()){
    console.log(v);// 输出: x 0 1 y
};

注意:

  1. function 关键字 和 函数名中间有一个 * 号
  2. 函数体内部使用 yield 表达式,定义不同的内部状态
  3. 需要注意的是,yield 不能跨函数
  4. 箭头函数不能用做 generator
  5. Generator 构造函数并不是全局可用。Generator 的实例必须从生成器函数返回

方法使用

Generator.prototype.next(value)

该方法返回一个包含属性 done 和 value 的对象。也可以通过接受一个参数用以向生成器传值。 因为每个 next() 方法传入的值都是上一个 yield 表达式的返回值,所以第一个 next() 传入的值是无效的,只有第二次使用才会生效。

返回的对象包含两个属性:

  • value为返回的值
  • done布尔值, 如果Interator未遍历完毕, 他会返回false, 否则返回true;
function* fib(max) {
    let a = 0,
        b = 1,
        n = 0;
    while (n < max) {
        yield a;
        [a, b] = [b, a + b];
        n ++;
    }
}
let f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}

Generator.prototype.return(value)

返回给定的值并结束生成器。

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
let g = gen();
g.next();        // { value: 1, done: false }
g.return("foo"); // { value: "foo", done: true }
g.next();        // { value: undefined, done: true }
  • 如果生成器调用return(value),则生成器将保持在“完成”状态。
  • 如果没有提供参数,则返回对象的value属性值为undefined
  • 如果提供了参数,则参数将被设置为返回对象的value属性的值。

Generator.prototype.throw(exception)

  • 可以在函数体外抛出错误,然后在 Generator 函数体内捕获。
  • 如果没有在 Generator 函数体内设置 try... catch 代码块,那么抛出的错误会直接被外部的 catch 代码块捕获,如果 Generator 函数体内部和外部都没有部署 try...catch 代码块,那么程序将报错,直接中断执行。
  • throw 抛出的错误如果想被内部捕获,最少需要执行一次 next 方法。如果没有执行就会直接被外部的 catch 代码块捕获。
function* gen() {
  try {
    yield;
  } catch (e) {
    console.log('内部捕获', e)
  }
}
let i = gen();
i.next();
try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('外部捕获', e);
}