Node.js - wait for multiple async calls

Solution 1:

I'm a big fan of underscore/lodash, so I usually use _.after, which creates a function that only executes after being called a certain number of times.

var finished = _.after(2, doRender);

asyncMethod1(data, function(err){
  //...
  finished();
});

asyncMethod2(data, function(err){
  //...
  finished();
})

function doRender(){
  res.render(); // etc
} 

Since javascript hoists the definition of functions defined with the function funcName() syntax, your code reads naturally: top-to-bottom.

Solution 2:

Assuming you want to run the two operations in parallel rather than waiting for one to finish before starting the next, you'll need to track how many operations have completed in each callback.

In raw node.js javascript, one way to do this would be this:

exports.init = function(req, res){
    var NYLakes = null;
    var NJLakes = null;
    var filterNY = {"State" : "NY"};

    db.collection('lakes').find(filterNY).toArray(function(err, result) {
        if (err) throw err;
        NYLakes = result;
        complete();
    });

    var filterNJ = {"State" : "NJ"};
    db.collection('lakes').find(filterNJ).toArray(function(err, result) {
        if (err) throw err;
        NJLakes = result;
        complete();
    });

    function complete() {
        if (NYLakes !== null && NJLakes !== null) {
            res.render('explore/index', {
                NYlakes: NYLakes,
                NJlakes: NJLakes
            });
        }
    }

};

Basically what's happening here is that you check at the end of each operation if all of them have finished, and at that point you finish off the operation.

If you're doing a lot of these things, take a look at the async library as an example of a tool to make it easier to manage this sort of thing.

Solution 3:

You can use async module:

var states = [{"State" : "NY"},{"State" : "NJ"}];

var findLakes = function(state,callback){
  db.collection('lakes').find(state).toArray(callback);
}

async.map(states, findLakes , function(err, results){
    // do something with array of results
});

Solution 4:

Wait.for https://github.com/luciotato/waitfor

using Wait.for:

exports.init = function(req, res){

    var NYLakes = {};
    var NJLakes = {};

    var coll = db.collection('lakes');

    var filterNY = {"State" : "NY"};
    var a = wait.forMethod(coll,'find',filterNY);
    NYLakes = wait.forMethod(a,'toArray');

    var filterNJ = {"State" : "NJ"};
    var b = wait.forMethod(coll,'find',filterNJ);
    NJLakes = wait.forMethod(b,'toArray');

    res.render('explore/index',
        {
            NYlakes: NYLakes,
            NJlakes: NJLakes
        }
    );

};

Requesting in parallel using wait.for parallel map:

exports.init = function(req, res){

    var coll = db.collection('lakes');

    //execute in parallel, wait for results
    var result = wait.parallel.map(
                    [{coll:coll,filter:{"State" : "NY"}}
                    , {coll:coll,filter:{"State" : "NJ"}}]
                    , getData);

    res.render('explore/index',
        {
            NYlakes: result[0],
            NJlakes: result[1]
        }
    );

};

//map function
function getData(item,callback){
try{
    var a = wait.forMethod(item.coll,'find',item.filter);
    var b = wait.forMethod(a,'toArray');
    callback (null, b);
} catch(err){
    callback(err);
}

I'm not familiar with mongo, so you may have to adjust the calls.