什么是ES6生成器,如何在node.js中使用它们?

今天我在一个node.js聚会上,有一个我在那里遇到的人说node.js有es6生成器。 他表示,这是一个比callback风格编程的巨大改进,并且会改变节点格局。 Iirc,他说了一些关于调用栈和exception的东西。

我查看了他们,但是还没有真正发现任何能够以一种对初学者友好的方式解释他们的资源。 什么是生成器的高级概述,和callback有什么不同(或更好?)?

PS:如果你可以给出一段代码来突出显示常见场景(做一个http请求或数据库调用)的区别,那真的很有帮助。

发电机,光纤和协同程序

“发电机”(除了“发电机”)也是“纤维”或“协同”的基本build筑块。 通过光纤,可以“暂停”等待asynchronous调用的函数返回,从而有效地避免了“就地”声明一个callback函数并创build一个“闭包”。 告别回拨地狱。

closures和尝试抓住

他说了一些关于调用栈和exception的东西

“closures”的问题是,即使他们“魔术般地”保留了callback的局部variables的状态,“closures”也不能保持调用堆栈。

在callback的时候,调用函数通常很久以前就已经返回了,所以调用函数的任何“catch”块都不能在asynchronous函数本身或callback中捕获exception 。 这是一个很大的问题。 因此,您不能将callback+闭包与exception捕获结合起来。

等待

…并会改变节点的景观

如果你使用生成器来构build像Wait.for-ES6这样的助手库(我是作者),你可以完全避免callback和闭包,现在“catch块”按预期工作,而且代码很简单。

如果你可以给出一段代码来突出显示常见场景(做一个http请求或一个数据库调用)的区别,那真的很有帮助。

检查Wait.for-ES6示例,查看具有callback和基于生成器的光纤的相同代码。

发电机是即将到来的ES6的许多function之一。 所以将来有可能在浏览器中使用它们(现在你可以在FF中使用它们)。

生成器是迭代器的构造函数。 听起来像乱码,所以更容易的是他们允许创build对象,稍后将可能使用.next()方法迭代循环。

发电机的定义与function类似。 除了他们有*yield他们。 *是告诉这是发电机,产量类似于返回。

例如,这是一个生成器:

 function *seq(){ var n = 0; while (true) yield n++; } 

然后你可以用var s = seq()来使用这个生成器。 但是与函数不同的是,它不会执行所有的操作并给出结果,它只会实例化生成器。 只有当你运行s.next() ,生成器才会被执行。 这里的yield与return类似,但是当yield会运行时,它会暂停生成器并在下一个expression式之后继续处理下一个expression式。 但是当下一个s.next()被调用时,生成器将会继续执行。 在这种情况下,它将继续执行while循环。

所以你可以迭代这个

 for (var i = 0; i < 5; i++){ console.log( s.next().value ) } 

或者与发电机的特定结构相关联:

 for (var n of seq()){ if (n >=5) break; console.log(n); } 

这些是关于生成器的基础知识(你可以看看yield*next(with_params)throw()和其他额外的构造)。 请注意,这是关于ES6中的生成器(所以你可以在节点和浏览器中完成这一切)。

但是这个无限序列与callback有什么关系呢?

重要的是,产量暂停发电机。 所以想象你有一个非常奇怪的系统,这样工作:

你有用户的数据库,你需要find一个ID的用户的名字,那么你需要在你的文件系统中检查这个用户的名字的关键,然后你需要连接到一些FTP用户的ID和密钥和连接之后做些事情。 (听起来很可笑,但我想显示嵌套的callback)。

以前你会写这样的东西:

 var ID = 1; database.find({user : ID}, function(userInfo){ fileSystem.find(userInfo.name, function(key){ ftp.connect(ID, key, function(o){ console.log('Finally '+o); }) }) }); 

callback里面的callback里面是callback里面的callback。 现在你可以写下如下的东西:

 function *logic(ID){ var userInfo = yield database.find({user : ID}); var key = yield fileSystem.find(userInfo.name); var o = yield ftp.connect(ID, key); console.log('Finally '+o); } var s = logic(1); 

然后用它with s.next(); 如你所见,没有嵌套的callback。

因为节点大量使用嵌套的callback函数,所以这就是为什么这个人告诉发电机可以改变节点景观的原因。

生成器是两件事的组合 – 一个Iterator和一个Observer

迭代器

迭代器是被调用时返回一个可迭代的东西。 从ES6开始,所有集合(Array,Map,Set,WeakMap,WeakSet)都符合Iterable契约。

生成器(迭代器)是生产者。 在迭代中,消费者PULL从生产者获得价值。

例:

 function *gen() { yield 5; yield 6; } let a = gen(); 

每当你调用a.next() ,你基本上都会从Iterator获得值,并在yield pause执行。 下一次调用a.next() ,执行将从之前的暂停状态恢复。

观察

生成器也是一个观察者,使用它可以将一些数值发送回生成器。 用例子更好地解释。

 function *gen() { document.write('<br>observer:', yield 1); } var a = gen(); var i = a.next(); while(!i.done) { document.write('<br>iterator:', i.value); i = a.next(100); } 

要在节点中使用ES6生成器,您需要安装节点 > = 0.11.2或iojs 。

在节点中,您将需要引用和声标志:

 $ node --harmony app.js 

或者你可以明确地引用生成器标志

 $ node --harmony_generators app.js 

如果你已经安装了iojs,你可以省略和谐标志。

 $ iojs app.js 

有关如何使用生成器的高级概述,请查看这篇文章 。