简体   繁体   中英

Are All Node “callback” Functions Potentially Asynchronous?

I'm a (relative) node newbie getting in the system, and all the enthusiasm in the community for "just write callbacks, everything's asynchronous and event driven, don't worry!" has left me a little confused as to the control flow within a single program (or in more node-ish terms, the control flow during the handling of a single request in a larger program)

If I have the following program running under node

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

Is there any chance that foo will execute after bar ? When I run the program via the command line locally, it's always

Called Foo
Called Bar

but I see so many warnings from well intended evangelists along the lines of don't assume your callback will be called when you think it will , that I'm unclear if they're just warning me about library implementation details, or if node.js does something weird/special when you use a function object as parameter.

No, there's no chance. Not for that code.

If you're writing your own functions, or if you have access to the code, you don't need to assume, you know whether everything's synchronous or otherwise, but if you don't have access to the code, or haven't yet read it, then no, you can't assume callbacks are going to be synchronous.

It's however bad practice to make assumptions like that for two reasons, first is that just because it's synchronous now doesn't mean somebody else, or forgetful future you can't change it later, and secondly, because if it's all synchronous, why are you/they using callbacks in the first place? The entire point of callbacks is to allow for the possibility of asynchronous calls. Using callbacks and then acting like they're always going to be synchronous, even if you know that's the case, makes your code confusing for anybody else coming in.

No

Your sample code is 100% synchronous, single-threaded, simple top-to-bottom. But that's because you don't do any I/O, don't have any real asynchronous calls, and don't use process.nextTick , setTimeout , or setInterval . To more realistically simulate async calls do something like:

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

Run that a few times and you'll see out-of-order results sometimes.

Is there any chance that foo will execute after bar?

In your current code, no. Although your doTheThing function has an asynchronous function signature (ie it takes a callback as the last argument, which to an outsider with no knowledge about the function's implementation would suggest that it's asynchronous), it's actually fully synchronous, and callback will be called without yielding to the runtime.

However

You really have no reason to give your doTheThing code an asynchronous signature, unless you're accommodating for introducing real async behavior into doTheThing at some point. And at that point, you have a problem, because the order in which foo and bar are called will flip.

In my opinion, there are only two good ways of writing code like you do: Either make it set in stone that doTheThing will be synchronous (most importantly: that it won't be dependent on I/O), which means that you can simply return from the function:

doTheThing = function(arg1){
   return null
};
doTheThing()
foo()
bar()

or change the stub implementation of doTheThing directly to include a call to setImmediate , ie

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

Note that this can also be written as

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

but that's just because at this moment, callback does not take any arguments. The first version is more close to what you had.

As soon as you do this, bar will always be called before foo , and it has now become safe to introduce async functionality into doTheThing .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM