所有节点“回调”函数是否可能是异步的?

时间:2013-08-09 05:04:24

标签: javascript node.js callback

我是一个(相对)节点新手进入系统,社区中所有的热情“只是写回调,一切都是异步和事件驱动,不用担心!”让我对单个程序中的控制流程感到有些困惑(或者在更多的节点术语中,在更大程序中处理单个请求期间的控制流程)

如果我在节点

下运行以下程序
var foo = function(){
    console.log("Called Foo");
};

var bar = function(){
    console.log("Called Bar");
};

var doTheThing = function(arg1, callback){
    callback();
};

doTheThing(true, function() {
    foo();
});
bar();

foo <{strong> bar之后<{>}} 当我在本地通过命令行运行程序时,它总是

Called Foo
Called Bar

但是我看到很多来自意图良好的福音传播者的警告,除了之外,不要以为你的回调会在你认为时被调用,我不清楚他们是否只是警告关于库实现细节,或者当使用函数对象作为参数时,node.js做了一些奇怪/特殊的事情。

3 个答案:

答案 0 :(得分:5)

不,没有机会。不是那个代码。

如果您正在编写自己的函数,或者您可以访问代码,则无需假设,您知道所有内容是同步还是其他,但如果您无法访问代码,或者尚未阅读,那么不,你不能认为回调将是同步的。

然而糟糕的做法做出这样的假设有两个原因,首先是因为它现在同步并不意味着其他人,或者健忘的未来你以后不能改变它,并且其次,因为如果它是全部同步的,你/他们为什么首先使用回调?回调的整个要点是允许异步调用的可能性。使用回调然后表现得像是他们总是同步的,即使你知道这种情况,也会让你的代码混淆其他任何人进来。

答案 1 :(得分:2)

没有

您的示例代码是100%同步,单线程,从上到下简单。但那是因为你没有做任何I / O,没有任何真正的异步调用,也没有使用process.nextTicksetTimeoutsetInterval。为了更真实地模拟异步调用,请执行以下操作:

function fakeAsync(name, callback) {
  setTimeout(function () {
    callback(null, name);
  }, Math.random() * 5000);
}

function logIt(error, result) {
  console.log(result);
}

fakeAsync('one', logIt);
fakeAsync('two', logIt);
fakeAsync('three', logIt);

运行几次,有时你会看到无序结果。

答案 2 :(得分:2)

  

有没有机会在棒之后执行foo?

在您当前的代码中,没有。虽然你的doTheThing函数有一个异步函数签名(即它将回调作为最后一个参数,对于一个不知道函数实现的局外人会认为它是异步的),它实际上是完全同步的,并且{{将调用1}}而不会屈服于运行时。

然而

你真的没有理由给你的callback代码提供异步签名,除非你在某些时候将真正的异步行为引入doTheThing。此时,您遇到了问题,因为调用doTheThingfoo的顺序将会翻转。

在我看来,只有两种编写代码的好方法:要么使它bar同步(最重要的是:它不依赖于I / O) ),这意味着你可以简单地从函数返回:

doTheThing

或直接更改doTheThing = function(arg1){ return null }; doTheThing() foo() bar() 的存根实施,以包含对doTheThing的调用,即

setImmediate

请注意,这也可以写为

var doTheThing = function(arg1, callback){
   setImmediate(function() { callback(); );
};

但这只是因为此时回调不会带任何参数。第一个版本更接近你的版本。

一旦执行此操作,var doTheThing = function(arg1, callback){ setImmediate(callback); }; 将始终在bar之前调用,现在可以安全地将异步功能引入foo