Conceptually, you just need to keep track of the number of async calls returning you are waiting for, which I've illustrated in your code below. But there are loads of little libraries that make this a lot more generic, elegant and give you more control (like making sure they run in sequence and similar stuff). async.js for instance.
load('application');
action('index', function () {
var asyncCounter = 2; // Or whatever number of async you want to complete...
var completeFunc = function() {
if (--asyncCounter > 0) {
return;
}
// rendering index template
render('index', { /* using async params */ });
};
someMethodWithAsyncCallback({}, function () {
/* async preparing some params for template */
completeFunc();
});
anotherMethodWithAsyncCallback({}, function () {
/* async preparing another params for template */
completeFunc();
});
// another couple of async calls
});