所有节点都是“;回调”;潜在异步功能

Are All Node "callback" Functions Potentially Asynchronous?

本文关键字:异步 功能 回调 节点      更新时间:2023-09-26

我是一个(相对的)节点新手,社区里对"只写回调,一切都是异步和事件驱动的,别担心

如果我有以下程序在节点下运行

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();

bar之后,foo是否有执行的机会?当我在本地通过命令行运行程序时,它总是

Called Foo
Called Bar

但我看到了很多来自善意的传道者的警告,比如不要认为你的回调会在你认为会被调用的时候被调用,我不清楚他们是在警告我库实现的细节,还是node.js在你使用函数对象作为参数时做了一些奇怪/特别的事情。

不,没有机会。不适用于该代码。

如果你正在编写自己的函数,或者你有权访问代码,你不需要假设,你知道一切是否同步,但如果你没有访问代码的权限,或者还没有阅读它,那么不,你不能假设回调将是同步的。

然而,做出这样的假设是一种糟糕的做法,原因有两个,第一,仅仅因为它现在是同步的并不意味着其他人,也不意味着你以后不能改变它,第二,因为如果它都是同步的,你/他们为什么一开始就使用回调?回调的全部目的是允许异步调用的可能性。使用回调,然后表现得好像它们总是同步的,即使你知道是这样,也会让你的代码让其他人感到困惑

您的示例代码是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);

运行几次,有时你会看到不正常的结果。

foo有可能在bar之后执行吗?

在当前代码中,没有。尽管doTheThing函数有一个异步函数签名(即,它将回调作为最后一个参数,对于不了解函数实现的局外人来说,这意味着它是异步的),但它实际上是完全同步的,并且callback将在不向运行时让步的情况下被调用。

但是

您真的没有理由给doTheThing代码一个异步签名,,除非您愿意在某个时候将真正的异步行为引入doTheThing。在这一点上,你有一个问题,因为foobar的调用顺序会翻转。

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

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之前被调用,并且现在可以安全地将异步功能引入doTheThing