Azevedo
Azevedo

Reputation: 2189

How do I simultaneously call async functions and wait for all callbacks?

In nodeJS, how do I simultaneously call async functions and wait for all callbacks before proceed?

In the example bellow I want main to return all_results only when f1, f2 and f3 are done with the callback()

function main(callback){
  var all_results = [];
  f1(function(results){
    all_results.push(result)
  });

  f2(function(results){
    all_results.push(result)
  });

  f3(function(results){
    all_results.push(result)
  });

  // when all 3 calls are complete:
  callback(all_results)
}


function f1(callback){
  ...
  callback(results);
}

function f2(callback){
  ...
  callback(results);
}

function f3(callback){
  ...
  callback(results);
}

Not using promises.

Upvotes: 1

Views: 204

Answers (2)

Nick is tired
Nick is tired

Reputation: 7055

Add a boolean variable for each function and only return when all are set:

function main(callback){
  var all_results = [];
  var finished = [false, false, false];
  f1(function(results){
    all_results.push(result)
    finished[0] = true;
  });

  f2(function(results){
    all_results.push(result)
    finished[1] = true;
  });

  f3(function(results){
    all_results.push(result)
    finished[2] = true;
  });

  var t = setInterval(checkFinished,1000);
  function checkFinished(){
      if(finished[0] && finished[1] && finished[2]){
          clearInterval(t)
          callback(all_results)
      }
  }
}

Upvotes: 0

trincot
trincot

Reputation: 350034

You cannot return an asynchronously retrieved result in a synchronous way. So the function main cannot return the results. You should stick to the asynchronous method you have chosen. So with callbacks, you should also provide a callback function when invoking main:

function main(callback){
    var all_results = [];

    function collect(results) {
        all_results.push(results);
        // when all 3 calls are complete:
        if (all_results.length === 3) callback(all_results);
    }

    f1(collect);
    f2(collect);
    f3(collect);
}

Call like this:

main(function (all_results) {
    console.log(all_results);
});

As others have stated, promises lead to nicer code in such situations.

How to do it with promises

Let's say you cannot modify functions f1, f2, f3, then you could create promisified versions of them with one simple helper function. Once that is done, the ES6-included method Promise.all will do the rest of the job, so the code becomes quite concise.

Here is a snippet that defines some dummy functions f1, f2, f3 which use setTimeout to get the asynchronous effect:

function promisify(f) {
    return new Promise(f);
}

function main(){
    return Promise.all([f1, f2, f3].map(promisify));
}

main().then(function (results) {
    console.log(results);
});

function f1(callback){
    setTimeout(function () {
        callback('result from f1');
    }, 100);
}

function f2(callback){
    setTimeout(function () {
        callback('result from f2');
    }, 500);
}

function f3(callback){
    setTimeout(function () {
        callback('result from f3');
    }, 200);
}

Upvotes: 5

Related Questions