JS生成器是什么?

107 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

我们知道JS中的函数一旦开始执行,就会一直执行到结束,期间就算有阻挡,也只是延缓一点时间而已,并不能让函数停止运行。

ES6中引入了一个新的函数类型,它并不符合这种特性,这类新的函数就是生成器。

var x = 1; 
function foo() { 
 x++; 
 bar(); // <-- 这一行是什么作用?
 console.log( "x:", x );
} 
function bar() { 
 x++; 
} 
foo(); // x: 3 

在这个例子中,我们确信 bar()会在x++和console.log(x)之间运行。但是,如果bar() 并不在那里会怎样呢?显然结果就会是2,而不是3。

我们写这样一个foo函数,带号表示他是一个生成器函数,这个号有其他的写法比如function* foo() 甚至你开心还可以这样写function*foo(),这个空格加在哪里,加不加都可以。全凭你喜欢,那么怎么让bar()在foo()中间运行呢。

var x = 1; 
function *foo() { 
 x++; 
 yield; // 暂停!
 console.log( "x:", x ); 
} 
function bar() { 
 x++; 
} 

构造一个迭代器it来控制这个生成器,next是控制生成器进行下一步的操作,第一个next启动生成器运行到field处截止。这时x为2,趁生成器暂停,执行bar()函数,这样就实现了在foo中执行bar,执行完bar之后再次调用next让函数继续执行,直到下一个field

var it = foo(); 
// 这里启动foo()!
it.next(); 
x; // 2 
bar(); 
x; // 3 
it.next(); // x: 3 

生成器函数称为函数,所以他和普通函数一样,可以接受参数,可以有返回值,但是有一个区别那就是生成器的函数需要调用next()才能开始执行,而返回的值是一个对象,对象的value属性就是函数的返回值。那这个东西有什么用吗,好像看起来花里胡哨的,没什么实际用处呀。

其实生成器提供了强大的内建消息输入输出系统,通过yeild和next()实现。考虑以下:

function *foo(x) { 
 var y = x * (yield); 
 return y; 
} 
var it = foo( 6 ); 
// 启动foo(..) 
it.next(); 
var res = it.next( 7 ); 
res.value; // 42 

这里执行一次next开启生成器函数,再执行了第二次next,这次传入了7作为参数,函数中可以接收到参数,返回值的value属性就是生成器函数的return。这里看到一个yield,两个next,生成器的执行是这样的,第一个next用于开启生成器,运行到yield前停止,第二个next就从yeild开始运行,到第三个yield前停止。