1

I am taking a look at Node.js' async module to solve an issue. I have implemented a little test:

var async = require("async");

function print(val) {
    console.log(val);
}

async.parallel([
    function(cb){ print(1); cb(null); },
    function(cb){ print(2); cb(null); },
    function(cb){ print(3); cb(null); },
    function(cb){ print(4); cb(null); },
    function(cb){ print(5); cb(null); }
],
    function(err) {
        if ( err ) {
            console.error(err);
            return;
        }
        console.log("Done!");
    }
);

console.log("Trulu");

Can I be sure that the Trulu log call will never be called before the call to async.parallel is finished? In other words, are all the functions and the final callback called before the Trulu log is called for sure?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
  • 1
    This won't answer your question and I am aware of that, but i want to say that you could wrap up your stuff in a promise to ensure it – Wottensprels Feb 07 '14 at 15:16
  • I guess if you want to insure that it is after the callbacks you can have your function write the log in the Callback. But I don't see a difference between that and the Done! log – CubanAzcuy Feb 07 '14 at 15:17
  • if you're stuck on async.js, it has a series method that does what you want it to do. – Arjun Mehta Feb 07 '14 at 15:39
  • @Arjun But does it guarantee that Trulu will be called after async.series will be finished? It does not seem so according to the provided answers. – Jérôme Verstrynge Feb 07 '14 at 15:41
  • @JVerstry Bergi's answer is the best in describing what happens. As he says, yes the call to log Trulu will be "called" after the async.parallel, but will return faster and thus will log Trulu before your async.parallel calls are done. – Arjun Mehta Feb 07 '14 at 15:55
  • 2
    @JVerstry If you want to do synchronous execution of stuff, look into promises, OR you have to wrap your call to log Trulu in the async.series call OR execute it in the final call back of your async.parallel call. – Arjun Mehta Feb 07 '14 at 15:56

3 Answers3

3

Short answer: No they are asynchronous.

Long answer:

As soon as you do anything asynchronous, inside the parralel callbacks, you can expect Trulu to get called immediatly after. When you call cb, it announce that the function returned. When all function have returned it will call the last callback.

So for that reason, if you do a setTimeout without a time and each functions are returning without anything asynchrone, they might behave as synchronous because Javascript only runs one unit at a time. And since they are probably started in order, it will look as if it run synchronously.

Take this as an example:

var looping = true;

setTimeout(function () {looping = false;}, 1000);

while(looping) {
   ...
}

This code will never leave as setTimeout will never get triggered. In async.parralel, I'm almost certain that each callbacks are going to get executed after the current unit is finished running. Which means, that you'll always have your console.log("Trulu") executed before any of the callbacks inside async.parralel.

Note

If you're looking for to do synchronous things, you shouldn't use a module called async. It's quite obvious that it's going to be asynchronous.

That said, I feel that there is a big misunderstanding with the term async. Asynchronous isn't a synonym for parralelism or having things simultaneous. Asynchrone, just means "without order in time". It could be simultaneous but it could be also sequential. To run asynchronous code on Javascript, you have to rely on an event loop.

The event loop can only process one event at a time, setTimeout is just putting the event on the loop. As soon as an event handler leaves, it gets to the next event and so on.

In your case, since you're not calling cb inside an event handler, they will be called synchronously as they never leave the current event.

Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99
  • 1
    They don't appear to be asynchronous with the [async library he's talking about](https://github.com/caolan/async) (oddly), not unless the functions themselves do something asynchronous. Neither experiment nor looking through the code suggests the functions are actually run in parallel *unless* the functions themselves do something asynchronous. E.g., `async.parallel` calls the functions *synchronously*, but I guess the assumption is they'd do async work. Frankly seems odd. – T.J. Crowder Feb 07 '14 at 15:27
  • @T.J.Crowder Yeah I know I'm explaining it – Loïc Faure-Lacroix Feb 07 '14 at 15:34
  • @T.J.Crowder: JS cannot run synchronous functions in parallel, so what would you expect :-) – Bergi Feb 07 '14 at 15:41
  • @Bergi: From a module called `async`? That it would fire them off asynchronously, e.g., via `nextTick` or `setTimeout`. I can see the sense of what it does, I just think I would have used a different name, seeing as the call doesn't do anything to make them parallel. – T.J. Crowder Feb 07 '14 at 15:47
  • 1
    @T.J.Crowder I see your point, it could have called each of the function inside a setTimeout instead of synchronously. – Loïc Faure-Lacroix Feb 07 '14 at 15:59
3

Can I be sure that the Trulu log call will never be called before the call to async.parallel is finished?

The call itself, yes.

In other words, are all the functions

I guess so, though the docs don't mention it. I would however expect no changes, since that is likely to break existing code.

and the final callback called before the Trulu log is called for sure?

No. You don't know the functions, and when they are asynchronous then the final callback will execute after Trulu. If all the functions are synchronous, you shouldn't use async anyway. Indeed, while async.js does use setImmediate internally so some callbacks (unfortunately hard to identify) will be "asynchronified", yet the maintainer stated:

Yes, making functions asynchronous is left up to the developer

If you want to ensure that callbacks are always called asynchronously (after Trulu), even when the functions are synchronous, you might consider using A+ compliant Promises.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • My first thought was you are wrong but the calls are all synchronous so `Trulu` is called after `Done!`. +1 – A1rPun Feb 07 '14 at 15:31
1

Since you're using async already, async has a series call. I think this may be what you're looking for, but a warning, this is ugly (because in my opinion async.js inherently makes for a mess):

async.series([
  parallel,
  function(callBack){ console.log("Trulu"); callBack(null); }
]);

function parallel(callBack){

  async.parallel([
    function(cb){ print(1); cb(null); },
    function(cb){ print(2); cb(null); },
    function(cb){ print(3); cb(null); },
    function(cb){ print(4); cb(null); },
    function(cb){ print(5); cb(null); }
  ],
    function(err) {
        if ( err ) {
            console.error(err);
            return;
        }
        console.log("Done!");
        callBack(null);
    }
  );
}

Honestly, I think the async module in general is making javascript so much more complicated than it is natively. If you'd like an answer that shows how to do what you want to do without using the async module at all, please let me know!

Another simpler solution would be to build that last call you want to execute after built into the callback of your async.parallel:

async.parallel([
  function(cb){ print(1); cb(null); },
  function(cb){ print(2); cb(null); },
  function(cb){ print(3); cb(null); },
  function(cb){ print(4); cb(null); },
  function(cb){ print(5); cb(null); }
],
  function(err) {          
      if ( err ) {
          console.error(err);
          return;
      }
      console.log("Done!");          
      console.log("Trulu"); // Executes after all your "parallel calls" are done
  }
);
Arjun Mehta
  • 2,500
  • 1
  • 24
  • 40