[ES6] Generator

Generator的语法

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。

下面代码定义了一个 Generator 函数testGenerator,它内部有两个yield表达式,即该函数有三个状态:a+1,a+2 和 a+3 。

function* testGenerator(a) {
  yield a + 1;
  yield a + 2;
  return a + 3;
}

var tg = testGenerator(2);

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
接下来,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

tg.next()   // { value: 3, done: false }
tg.next(3)   // { value: 5, done: false }
tg.next(4)   // { value: 7, done: true }
tg.next(2)   // { value: undefined, done: true }

以下的定义都是被允许的,但是我们通常采用第一种写法

function* foo(x, y) { ··· }
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function*foo(x, y) { ··· }

yield表达式

yield表达式只能用在Generator函数中,否则会报错。一下的代码把yield放在了forEach中,会报错,把forEach改成for循环即可。

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

yield表达式如果用在另一个表达式之中,必须放在圆括号里面。

function* demo() {
  console.log('Hello' + yield); // SyntaxError
  console.log('Hello' + yield 123); // SyntaxError
  console.log('Hello' + (yield)); // OK
  console.log('Hello' + (yield 123)); // OK
}

next方法

next方法可以带一个参数,该参数就会被当作Generator函数中,上一个yield表达式的返回值。
下面的代码中,第二次运行a.next()时候不带参数,导致 y 的值等于2 * undefined(即NaN),除以 3 以后还是NaN,因此返回对象的value属性也等于NaN。第三次运行a.next()时候不带参数,所以z等于undefined,返回对象的value属性等于5 + NaN + undefined,即NaN。
如果向next方法提供参数,返回结果就完全不一样了。上面代码第一次调用b.next()时,返回x+1的值6;第二次调用b.next(),将上一次yield表达式的值设为12,因此y等于24,返回y / 3的值8;第三次调用b.next(),将上一次yield表达式的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。

function* demo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = demo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = demo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

你可能感兴趣的:([ES6] Generator)