node.js modules:Async vs Fibers.promise vs Q_oper8

时间:2012-04-23 14:47:58

标签: multithreading node.js asynchronous node-modules

只是想知道是否有人可以给我比较这些模块之间的权衡以处理异步事件。具体来说,我有兴趣知道使用Async而不是Fibers.promise的原因,我现在至少在我的测试代码中使用得非常广泛。特别是,我在Fibers.promise中看到的一个主要优点是我可以保持堆栈链前面分叉,使得可以使用try { } catch { } finally,并且还允许我确保在处理请求之后响应已经结束。

是否有人使用Q_oper8?我在另一页上发现了这个,只是想知道这是否已经死了或者我应该检查一下。

4 个答案:

答案 0 :(得分:37)

我从来没有听说过Q_oper8,所以我无法评论它,但我会从另一个方向来看这个。我听说过async first和Fiber(及其帮助库)第二,我实际上并不喜欢后者。

纤维的下降

其他Javascript开发人员不熟悉

Fiber通过编译的Fiber本机方法向Javascript引入协同例程的概念,该方法接管传递给它的Javascript代码的解释,拦截对yield的调用以跳回到等待协同例程。

这对您来说无关紧要,但如果您需要在团队中工作,您必须向您的成员传授这个概念(或者希望他们对其他语言的概念有经验,比如Go)。

无Windows支持

因此,为了使用Fiber或其上编写的任何库,您必须首先为您的平台本地编译它。我不使用Windows,但请注意Windows上不支持光纤,因此限制了您自己的库的实用程序。这意味着您无法找到通过Fiber 编写的通用Node.js库(无论如何,您可能都不会这样做,因为它增加了一个代价高昂的编译步骤,你以其他方式避免异步)。

浏览器不兼容

这意味着您使用Fiber 编写的任何代码都不能能够在浏览器中运行,因为您无法将本机代码与浏览器混合(我也不会像浏览器用户那样想要即使您写的所有内容都是" Javascript" (它的语法是Javascript,但在语义上却没有。)

更难调试

虽然"回调地狱"可能在视觉上不那么令人愉悦,Continuation-Passing Style在Co-Routines上有一个非常好的事情 - 你确切地知道调用堆栈中出现问题的位置并且可以向后追踪。 Co-Routines在程序中的多个点上输入函数,并且可以退出三种调用returnthrowyield(),其中后者也是一个回归点。

使用协同例程,您可以在两个或多个运行"同时"的函数之间执行交叉执行,并且您可能在事件循环中同时运行多组协同例程。使用传统的回调,您可以保证在执行所述函数期间函数的外部范围是静态的,因此您只需要检查那些外部变量,如果需要的话。协同例程需要在每yield()之后运行这些检查(因为它与原始协同例程的使用将被转换为真实Javascript中的回调链)。

基本上,我认为协同例程概念更难以使用,因为它必须存在于Javascript事件循环的内部,而不是一个方法实施一个。

是什么让Async"更好"?

更糟糕的是

它更糟糕的是更好的"想法,实际上。而不是扩展Javascript语言来尝试摆脱它的疣(并在我看来创建新的疣),Async是一个纯粹的Javascript解决方案来掩盖它们,就像化妆一样。

显式控制流程

Async函数描述了需要跨越事件循环障碍的不同类型的逻辑流,并且库涵盖了实现该逻辑所需的回调代码的实现细节,并且您只是提供它应该大致运行的函数它们将在事件循环中执行的线性顺序。

如果您愿意放弃围绕异步方法的第一个缩进级别'参数,你没有额外的缩进与Co-Routines相比,只有少量额外的function(callback) {声明行,如下所示:

var async = require('async');
var someArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
async.forEach(someArray,
function(number, callback) {
    //Do something with the number
    callback();
}, function(err) {
    //Done doing stuff, or one of the calls to the previous function returned an error I need to deal with
});

在这种情况下,您知道您的代码所使用的所有变量只有在您的代码运行之前才能更改,如果它们没有被您的代码更改,那么您可以更容易地调试,并且只有一个"返回"机制:callback()。你可以在没有成功的情况下回调,也可以在出现问题时将回调传递给错误。

代码重用并不困难

上面的例子使代码重用变得困难,但并非如此。您始终可以将命名函数作为参数传递:

var async = require('async');

// Javascript doesn't care about declaration order within a scope,
// so order the declarations in a way that's most readable to you

async.forEach(someArray, frazzleNumber, doneFrazzling);

var someArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];

function frazzleNumber(number, callback) {
    // Do something to number
    callback();
}

function doneFrazzling(err) {
    // Do something or handle error
}

功能性,而非命令式

异步模块不鼓励使用命令式流控制,并鼓励(对于跨越事件循环的部分)需要使用函数进行流控制。

功能风格的优点是你可以轻松地重复使用你的循环体或你的条件,并且你可以创建新的控制流"动词"更好地匹配代码流(由异步库的存在证明),例如实现函数调用顺序的依赖图解析的async.auto控制流方法。 (您指定一系列命名函数并列出依赖于执行的其他函数(如果有),auto首先运行"独立函数,然后运行可以运行的下一个函数当其依赖函数完成运行时。)

不是编写代码以适应您的语言所指示的命令式样式,而是根据问题的逻辑来编写代码,并实现" glue"控制流程来实现它。

摘要

Fibre,就其扩展Javascript语言的本质而言,无法在Node.js中开发一个大型生态系统,尤其是当Async在外观部门获得80%的方式时,并且没有共同的其他缺点在Javascript。

答案 1 :(得分:11)

答案简短:

  • Async是一个用于管理单线程异步的纯/经典JavaScript解决方案
  • Fibers是用于创建协程的node.js扩展。它包括一个用于管理单线程异步的期货库。
  • 还有很多其他期货图书馆(如下所列)不需要扩展javascript。
  • Q_oper8是一个用于管理多进程并发的node.js模块

请注意,这些都没有提供"线程"所以没有人可以说做多线程(虽然也有一个node.js扩展名:threads_a_gogo)。

异步与光纤/期货

异步

异步和光纤/期货是解决同一问题的不同方法:管理异步解析依赖关系。异步似乎还有更多的钟声和口哨"比试图解决这个问题的许多其他图书馆,我认为这更糟糕(更多的认知开销 - 即更多的废话学习)。

在javascript中,基本的异步性如下所示:

asyncCall(someParam, function(result) {
   useThe(result);
});

如果你的情况需要的不仅仅是基本的异步性,比如你需要两个异步调用的结果,你可能会这样做:

asyncCall1(someParam, function(result0) {
  asyncCall2(someParam, function(result1) {
   use(result0, result1);
  }
});

已经开始看起来像回调地狱。它的效率也很低,因为第二个调用正在等待第一个调用完成,即使它不依赖于它,更不用说代码甚至不做任何合理的错误处理。 Async提供了一种解决方案,可以更有效地编写它:

async.parallel([
  function(callback) {
    asyncCall1(someParam, function(result0) {
      callback(null,result0);
    },
  function(callback) {
    asyncCall1(someParam, function(result1) {
      callback(null,result1);
    },
  }
],
function(err, results) {
  use(results[0], results[1]);
});

所以对我而言,这比回调地狱还要糟糕,但我认为对每个人来说都是如此。尽管它很难看,它允许两个调用同时发生(只要它们进行非阻塞IO调用或类似的事情)。 Async有更多用于管理异步代码的选项,因此如果您有兴趣,请查看the documentation

输入光纤/期货

协同程序Fibers模块包含一个期货库,它使用协同程序将异步事件重新注入当前的延续(future.wait())。

Fibers与大多数其他期货库不同,因为它允许当前延续等待异步事件 - 这意味着它不需要使用回调以便从异步请求中获取值 - 允许异步代码变得像同步一样。阅读有关协程的更多信息。

Node.js有像readFileSync这样的函数,它可以让你在为函数获取文件的同时在线等待函数。这不是通常在javascript中完成的事情,并且不是可以用纯JavaScript编写的东西 - 它需要像Fibers这样的扩展。

回到上面的相同异步示例,这就是光纤/期货的样子:

var future0 = asyncCall1(someParam);
var future1 = asyncCall2(someParam);
use(future0.wait(), future1.wait());

这非常简单,就像那里的异步混乱一样高效。它以优雅有效的方式避免了回调地狱。但是有(次要的)缺点。大卫·埃利斯夸大了许多缺点,所以我在这里重复唯一有效的一个:

浏览器不兼容

由于Fibers是node.js扩展名,它将与浏览器不兼容。这将使得使用node.js服务器和浏览器的共享代码无法使用光纤。但是,有一个强有力的论据,即您在服务器上需要的大多数异步代码(文件系统,数据库,网络调用)与浏览器所需的代码相同(ajax调用)。也许超时会发生碰撞,但这似乎就是这样。

除此之外,streamline.js项目还有能力弥补这一差距。看起来它有一个编译过程,可以使用同步和期货将streamline.js代码转换为使用回调样式的纯javascript,类似于现在不支持的Narrative Javascript。 Streamline.js可以在幕后使用几种不同的机制,一种是node.js Fibers,另一种是ECMAScript 6生成器,最后一种是转换为我已经提到的回调式javascript。

更难调试

这个似乎是一个有效的,如果是轻微的抱怨。即使您只是计划使用光纤/期货,而不是使用协同程序,但由于意外的功能退出(和入口)点,可能仍会出现令人困惑的上下文切换。

在javascript

中引入先发制人

这可能是光纤最主要的问题,因为它有可能(但不太可能)引入难以理解的错误。基本上,因为光纤yield可能导致一组代码临时退出到另一个未确定的函数,所以可能会读取或引入某些无效状态。有关详细信息,请参阅this article。就我个人而言,我认为纤维/期货和类似结构的令人难以置信的清洁度非常值得这个罕见的阴险错误。更多的错误是由糟糕的并发代码引起的。

抱怨无效

  • 不在Windows上:这不再是真的
  • 对协同程序不熟悉:A。不熟悉从来都不是回避某些事情的理由。无论你多么熟悉它,它的好处都是好的。 B.虽然协程和产量可能不熟悉,但期货是一个容易理解的概念。

其他期货图书馆

有许多图书馆实施期货,其概念可能被称为"期货","延期对象"或"承诺"。其中包括async-futurestreamline.jsQwhen.jspromiscuousjQuery's deferredcoolaj86's futures,{{3}等图书馆}和kriszyp's promises

其中大多数使用回调来解决期货,这解决了Fibers引入的许多问题。然而,它们并不像纤维/期货一样干净,因为它们比Async更清洁。以下是使用我自己的Narrative Javascript

再次使用相同的示例
var future0 = asyncCall1(someParam);
var future1 = asyncCall2(someParam);
Future.all([future0, future1]).then(function(results) {
  use(results[0], results[1])
}).done()

Q_oper8

Q_oper8真的是一个不同的野兽。它使用进程池在队列中运行作业。由于async-future *和javascript没有可用的本机线程,因此进程是利用node.js中多个处理器的常用方法。 Q_oper8旨在替代使用node.js的child_process模块​​管理进程。

答案 2 :(得分:2)

您还应该查看Step

它只处理异步可以做的一小部分,但我认为代码更容易阅读。这对于处理一系列事情的正常情况非常有用,其中一些事情并行发生。

我倾向于使用Step作为我的大部分逻辑,然后当我需要在串行或并行执行中重复应用方法时偶尔使用异步(即 - 调用此函数直到,或者在此数组的每个元素上调用此函数)。

答案 3 :(得分:1)

我在客户端使用jQuery的Deferred功能,而jQuery延迟服务器上的nodejs代替嵌套回调。它大大减少了代码并使事情变得如此可读。

http://techishard.wordpress.com/2012/05/23/promises-promises-a-concise-pattern-for-getting-and-showing-my-json-array-with-jquery-and-underscore/

http://techishard.wordpress.com/2012/05/29/making-mongoose-keep-its-promises-on-the-server/