考虑以下场景:
var a = 1;
var b = 2;
function foo(){
a++;
b = b * a;
a = b + 3;
}
function bar(){
b--;
a = 8 + b;
b = a * 2;
}
foo();
bar();
console.log(a, b); // 11 22
bar();
foo();
console.log(a, b); // 183 180
对于上面的两个函数foo和bar,它们中的任何一个,一旦开始了就会一直执行下去直至完毕.
倘若,我们想在foo中a++后,暂停一下在去执行bar中的某个语句… 可以使用ES6提供的yield语句.
改变如下:
var a = 1;
var b = 2;
function *foo(){ // *是生成器的标志
a++;
yield;
b = b * a;
a = (yield b) +3;
}
function *bar(){
b--;
yield;
a = (yield 8) + b;
b = a * (yield 2);
}
// 调用:foo执行完,在执行bar()
var s1 = foo(); // 初始化迭代器.
s1.next();
console.log(a, b); // 2 2
s1.next();
console.log(a, b); // 2 4
// 还有最后一个 a = (yield b) + 3;
s1.next(); // "预计"执行后会得到 7 4
console.log(a, b); // 实际上 NaN 4
// 诶????
// 实际执行yield b时,并得不到b的值因此会返回NaN. 于是a就是NaN
// 改进如下(foo中有2个yield, 因此会有3个next)
var s1 = foo();
var val1 = s1.next().value;
console.log(a, b);
val1 = s1.next(val1).value;
console.log(a, b);
val1 = s1.next(val1).value;
console.log(a, b);
// 运行bar,(bar中有3个yield,会有4个next调用)
var s2 = bar();
var val2 = s2.next().value;
console.log(a, b);
val2 = s2.next(val2).value;
console.log(a, b);
val2 = s2.next(val2).value;
console.log(a, b);
val2 = s2.next(val2).value;
console.log(a, b);
可以看到,和正常函数执行结果是一样的…
还注意到.上述好多赋值语句是重复的,因此可以考虑构造一个辅助函数step,用于将yield返回的值原样的传入.
// step
function step(gen){
var it = gen();
var last;
return function(){
last = it.next(last).value;
}
}
接下来使用step,先执行bar,后执行foo
var s1 = step(bar);
var s2 = step(foo);
s1(); // 1 1
s1(); // 1 1
s1(); // 9 1
s1(); // 9 18
s2(); // 10 18
s2(); // 10 180
s2(); // 183 180
回归主题,交替执行bar和foo
var s1 = step(foo);
var s2 = step(bar);
s2(); // b-- ,yield
s2(); // yield 8
s1(); // a++, yield
s2(); // a = 8 + b, yield 2
s1(); // b= b * a, yield b
s1(); // a = b + 3
s2(); // b = a * 2
可以看到,bar 和foo 都经过了多次的停止与启动,且它们之间进行了交替操作.
参考《你不知道的JavaScript》(中卷)P241~P242