在es6标准中允许使用function* 关键字创建一个生成器函数与之配对的是yield函数,在生成器好函数的内部每次调用yield的时候可以向迭代器的调用者返回一个值,并保存当前执行的位置,直到迭代器再次被调用
例子:
function* range(start,end) {
for(var i=start;i<end;i++){
yield i
}
}
var rangeit= range(10,20)
rangeit.next()
{value: 10, done: false}
rangeit.next()
{value: 11, done: false}
rangeit.next()
{value: 12, done: false}
rangeit.next()
{value: 13, done: false}
rangeit.next()
{value: 14, done: false}
rangeit.next()
{value: 15, done: false}
rangeit.next()
{value: 16, done: false}
rangeit.next()
{value: 17, done: false}
rangeit.next()
{value: 18, done: false}
rangeit.next()
{value: 19, done: false}
rangeit.next()
{value: undefined, done: true}
我们发现当yield被调用的时候迭生成器的函数好像被暂停了,就好像函数的执行上下文被保存了,等到迭代器再次被调用的时候函数的状态直接切换到之前的上下文,有编程经验的老手不难发现我们可以把生成器函数的一些中间状态手动的保存在一个闭包里面,可以获得类似的效果,不过当生成器内部的控制逻辑比较复杂的时候使用yield关键字会获得比较好的代码可读性,在babel转码方案中生成器函数的转码方案也是把生成器的一些中间状态抽取到闭包的一个变量中
上述代码使用babel转码之后的结果
var _marked = /*#__PURE__*/regeneratorRuntime.mark(range);
function range(start, end) {
var i;
return regeneratorRuntime.wrap(function range$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
i = start;
case 1:
if (!(i < end)) {
_context.next = 7;
break;
}
_context.next = 4;
return i;
case 4:
i++;
_context.next = 1;
break;
case 7:
case "end":
return _context.stop();
}
}
}, _marked, this);
}
var rangeit = range(10, 20);
我们发现生成器函数的一些变量被抽取到了闭包变量中,for循环被转换成了while、switch/case 语句,用递归变量_context来保存迭代执行的状态,最后使用regeneratorRuntime.wrap包裹成一个迭代器
再来看个复杂的例子
function* range(start,end) {
for(var i=start;i<end;i++){
for(var j=start;j<end;j++){
if(i==j){
yield [i,j]
}
}
}
}
var rangeit= range(10,20)
转码后
var _marked = /*#__PURE__*/regeneratorRuntime.mark(range);
function range(start, end) {
var i, j;
return regeneratorRuntime.wrap(function range$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
i = start;
case 1:
if (!(i < end)) {
_context.next = 13;
break;
}
j = start;
case 3:
if (!(j < end)) {
_context.next = 10;
break;
}
if (!(i == j)) {
_context.next = 7;
break;
}
_context.next = 7;
return [i, j];
case 7:
j++;
_context.next = 3;
break;
case 10:
i++;
_context.next = 1;
break;
case 13:
case "end":
return _context.stop();
}
}
}, _marked, this);
}
var rangeit = range(10, 20);
来个简单的例子
function* test(){
yield 1
yield 2
yield 3
yield 4
yield 5
}
var it= test()
转码后
function test() {
return regeneratorRuntime.wrap(function test$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return 1;
case 2:
_context.next = 4;
return 2;
case 4:
_context.next = 6;
return 3;
case 6:
_context.next = 8;
return 4;
case 8:
_context.next = 10;
return 5;
case 10:
case "end":
return _context.stop();
}
}
}, _marked, this);
}
var rangeit = test();
通过几个例子我们发现在babel方案中是通过在循环流程和yield调用中插入context check point来实现上下文切换的