首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么是ES6生成器,如何在node.js中使用它们?

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

Stack Overflow用户
提问于 2013-09-17 13:45:08
回答 5查看 6.7K关注 0票数 20

今天我参加了一个node.js聚会,我在那里遇到的人说node.js有es6生成器。他说,这是对回调风格编程的巨大改进,并将改变节点的格局。Iirc,他说了一些关于调用堆栈和异常的事情。

我查找了它们,但还没有找到任何以初学者友好的方式解释它们的资源。什么是生成器的高级概述,以及它们之间有何不同(或更好?)而不是回调?

PS:如果你能给出一段代码来突出常见场景(发出http请求或db调用)的区别,这将是非常有帮助的。

EN

回答 5

Stack Overflow用户

发布于 2014-02-16 04:36:29

生成器、纤程和协程

“生成器”(除了是“生成器”)也是"fibers" or "coroutines"的基本构建块。使用纤程,您可以“暂停”等待异步调用返回的函数,有效地避免“当场”声明回调函数和创建“闭包”。跟回调地狱说再见吧。

闭包和try-catch

...he谈到了调用堆栈和异常

“闭包”的问题是,即使它们“神奇地”为回调保留局部变量的状态,“闭包”也不能保留调用堆栈。

在回调时,通常调用函数已经返回了很长一段时间,因此调用函数上的任何" catch“块都不能捕获异步函数本身或回调中的异常。这带来了一个大问题。因此,您不能将callbacks+closures与异常捕获结合使用。

Wait.for

...and将改变节点环境

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

如果你能给出一段代码来突出显示常见场景(发出http请求或db调用)的区别,那将非常有帮助。

查看Wait.for-ES6示例,查看带有回调和基于生成器的纤程的相同代码。

更新2021年:所有这些都已被javascript/ES2020 async/await取代。我的建议是使用Typescript和async/await (它基于Promises,也是标准化的)

票数 16
EN

Stack Overflow用户

发布于 2014-07-26 10:50:47

生成器是即将到来的ES6中的many特性之一。因此,在the future中可以在浏览器中使用它们(现在你可以在FF中使用它们)。

生成器是迭代器的构造器。听起来像是胡言乱语,所以用更简单的术语来说,它们允许创建对象,稍后可以使用.next()方法使用类似于for循环的东西进行迭代。

生成器的定义方式与函数类似。除了他们里面有*yield。*是为了告诉这是生成器,收益率类似于返回。

例如,这是一个生成器:

代码语言:javascript
复制
function *seq(){
    var n = 0;
    while (true) yield n++;
}

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

因此,您可以使用以下命令进行迭代

代码语言:javascript
复制
for (var i = 0; i < 5; i++){
  console.log( s.next().value )
}

或者使用生成器的特定构造:

代码语言:javascript
复制
for (var n of seq()){
    if (n >=5) break;
    console.log(n);
}

这些是关于生成器的基础知识(您可以查看yield*next(with_params)throw()和其他其他构造)。请注意,它是关于ES6中的生成器的(因此您可以在节点和浏览器中完成所有这些操作)。

但是这个无限数序列和有什么关系呢?

这里重要的一点是,yield会暂停生成器。想象一下,你有一个非常奇怪的系统,它是这样工作的:

你有一个有用户的数据库,你需要找到一个具有某个ID的用户名,然后你需要在你的文件系统中检查这个用户名的密钥,然后你需要用用户的id和密钥连接到一些ftp,并在连接后执行一些操作。(听起来很可笑,但我想显示嵌套的回调)。

以前,你会写下这样的代码:

代码语言:javascript
复制
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);
        })
    })
});

这是回调内部的回调。现在您可以编写类似这样的代码:

代码语言:javascript
复制
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();,因为您看到没有嵌套的回调。

因为node大量使用嵌套回调,这就是为什么这个人告诉他生成器可以改变node的景观。

票数 10
EN

Stack Overflow用户

发布于 2015-08-17 06:03:51

生成器是两个东西的组合-一个Iterator和一个Observer

迭代器

迭代器是指当调用返回一个可迭代对象时,您可以对其进行迭代。从ES6开始,所有集合(数组、映射、集合、WeakMap、WeakSet)都符合迭代契约。

生成器(迭代器)是一个生成器。在迭代中,消费者PULLs来自生产者的值。

示例:

代码语言:javascript
复制
function *gen() { yield 5; yield 6; }
let a = gen();

无论何时调用a.next(),本质上都是从迭代器中获取-ing值,并在yield中执行pause。下次调用a.next()时,执行将从先前暂停的状态恢复。

观察者

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

代码语言:javascript
复制
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);
}

在这里,您可以看到yield 1的用法就像一个表达式,它的计算结果是某个值。它计算得到的值是作为参数发送到a.next函数调用的值。

因此,第一次i.value将是产生的第一个值(1),当继续迭代到下一个状态时,我们使用a.next(100)将一个值发送回生成器。

你可以在Node.JS的什么地方使用它?

生成器广泛用于spawn (来自taskJS或co)函数,该函数接受生成器,并允许我们以同步方式编写异步代码。这并不意味着异步代码被转换为同步代码/同步执行。这意味着我们可以编写看起来像sync的代码,但在内部它仍然是async

同步正在阻止;异步正在等待。编写阻塞的代码很容易。如果为PULLing,则值显示在赋值位置。如果为PUSHing,则值出现在回调的参数位置

当您使用迭代器时,您将PULL来自生产者的值。当您使用回调时,生产者PUSH将值es到回调的参数位置。

代码语言:javascript
复制
var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH

在这里,您从a.next()拉取值,在第二个中,v => {...}是回调函数,一个值被PUSH到回调函数的参数位置v中。

使用这种pull-push机制,我们可以像这样编写异步编程,

代码语言:javascript
复制
let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
  // wait for 100 ms and send 1
  let x = yield delay(100).then(() => 1);
  console.log(x); // 1

   // wait for 100 ms and send 2
  let y = yield delay(100).then(() => 2);
  console.log(y); // 2
});

所以,看看上面的代码,我们编写的异步代码看起来像是blocking ( that语句等待100ms,然后继续执行),但实际上它是waiting。generator的pauseresume属性允许我们实现这个神奇的技巧。

它是如何工作的?

派生函数使用yield promise从生成器中提取promise状态,等待直到promise被解析,并将解析后的值PUSHes回生成器,以便它可以使用它。

现在就使用它

因此,使用生成器和派生函数,您可以清理NodeJS中的所有异步代码,使其看起来和感觉上是同步的。这将使调试变得容易。同样,代码看起来也会很整洁。

顺便说一句,这是针对ES2017的JavaScript原生版本--作为async...await。但是您现在可以在ES2015/ES6和ES2016中使用它们,使用库中定义的派生函数- taskjs、co或bluebird

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18842105

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档