-1

I was referring this nodetuts tutorial on coordinating parallel calls in NodeJs. http://nodetuts.com/series/asynchronous-programming/mastering-asynchronous-programming-02.html

There is something that I needed your help in understanding.

callback1,callback12, callback3 are the functions that need to be executed parallelly. Each takes their own time to execute.

But their results are stored in the results array in the respective order

[resultofcallback1, resultofcallback2, resultofcallback3]

The only contract of the final callback to execute only once and show the results in a proper order, as mentioned. For that we have a Boolean calledBack to make sure it is executed only once.

Please see the code below

My argument is while executing callBack1. Inside handleResult : pending =0, order = 0, pending is incremented to 1 Let's say this callback takes the least time to execute.

Inside the return function for this handleResult pending is decremented which makes pending 0. Let's say by this time no other callBacks(callback1 ,callback2) has reached their handlResult function, thus pending remains 0. So this if(!pending) assert and the final callBack is called callback(null, results); outputting only one result something like [resultofCallback1,'',''], the rest two empty, as the final callback should be called only once.

module.exports = function composedCall(args, cb){

  var pending = 0;
  var results = [];
  var calledBack = false;

  callback1(args,handleResult());
  callback2(args,handleResult());
  callback3(args,handleResult());

  function handleResult(){
    var order = pending;
    pending ++;
    return function(err, result){
      pending --;
      if(err){
        callback(err);
      }else{
        results[order] = result;
        if(!pending){
          callback(null, results);
        }
      }
    }
  }
  function callback(err, value){
    if(! calledBack){
      calledBack = true;
      cb(err, value)
    }
  }
}


function callback1(args ,callback){
  setTimeout(callback, randomTimeout(), null, 1);
}

function callback2(args ,callback){
  setTimeout(callback, randomTimeout(), null, 2);
}
function callback3(args ,callback){
  setTimeout(callback, randomTimeout(), null, 3);
}

//utils
function randomTimeout(){
  return Math.floor(Math.random() * 1e3);
}

function randomValue(){
  return Math.floor(Math.random() * 1e10);
}

Is this a right approach to make coordinating parallel call Did I miss something?

nitte93
  • 1,820
  • 3
  • 26
  • 42
  • What you're missing is that you're calling `handleResult()` synchronously so the scenario you're describing can never happen. Pending will be incremented 3 times before even the first async function executes. – slebetman Jul 16 '16 at 14:33
  • Remember, the `setTimeout`s will not be calling `handleResult()`. They'll be calling the functions `handleResult()` return. It's those functions that will be executed asynchronously, `handleResult()` is executed synchronously to generate them. – slebetman Jul 16 '16 at 14:37
  • OK, Now It makes sense. I'm sorry I seriously missed that point. What if the setTimwout for callback1 is set to 0. Would that make any difference? I know it's a callback, just curious! – nitte93 Jul 16 '16 at 14:45
  • 1
    setTimeout always calls its callback async even if it is set to 0. It will simply call it on the next tick. The difference between `setTimeout` and `process.nextTick` is that `setTimeout` is processed with all async events including network and disk-io processes. `process.nextTick` is processed before all other events so if you call `process.nextTick` in an infinite loop you may block all other processes (I found that out the hard way wondering why my packets were never sent out) – slebetman Jul 17 '16 at 00:48
  • Thank you. That was really helpful. – nitte93 Jul 17 '16 at 15:55

1 Answers1

0

You can always use async parallel or stuff like that.

As for the manual implementation, you can do one of the following:

1- Long poll the amount of finished tasks. If # of finished tasks == total # of tasks => final callback

2- At the end of every individual callback, send an "I'm done" signal and check the amount of "I'm done" signals. If the amount of "I'm done" signals is equal to total number of tasks => final callback.

Looks like you're going with 2nd approach. Checking your code, I don't see you missing any aspect, but you have one big problem:

If your first task finishes before you register the second task, you execute the final callback only with the result of the first task.

To restate: The way you register your tasks brings up a race condition that highly depends on the fact that you roll high enough from random timeouts so that you can register all your tasks before the ones that you registered all finish.

Suggested fix: Don't start any of your tasks before you successfully register all of them.

Also: If you start your pending at 0 and execute order = pending, you get a pending = 2 when you register 3 tasks and none of them finish, and after the second task finishes and the 3rd task is still under way, you execute the final callback.

ardilgulez
  • 1,856
  • 18
  • 19