The correct answer in this case is to use async, like Yogesh Katri mentioned. It's pretty simple to use and read.
If you need some more flexibility I suggest you take a look at Q, which works with promise objects. This allows you to have a generic implementation for handling async calls across your code.
// Create for each task a _promise_, execute each of them in parallel
// and let them `resolve` their promise in the callback while you're
// waiting on the _main_ flow with the `Q.all` method.
var Q = require('q');
// Wrapper to create promises
function promiseMe(fn) {
var defer = Q.defer();
fn(defer);
return defer.promise;
}
// Create a fake async task (via setTimeout)
function fakeTask(desc, delay, result) {
return promiseMe(function(defer) {
console.log('Executing task ' + desc + ' ...');
setTimeout(function() {
console.log('Done executing task ' + desc + '.');
defer.resolve(result);
}, delay);
});
}
// Create an array of promises for all our tasks
var promises = [
fakeTask('user data', 2000, { user: 'hansch' }),
fakeTask('user orders', 1000, [ { order: 1 }, { order: 2 } ]),
fakeTask('something else', 3000, { magic: true })
];
console.log('Waiting for all tasks to complete ...');
// Tell Q to wait for all promises to resolve
Q.all(promises).then(function(result) {
console.log('All done.');
console.log('User data', result[0]);
console.log('User orders', result[1]);
console.log('Something else', result[2]);
});