Use q.async and generators to simplify the control flow

Managing asynchronous tasks in Javascript can be hard. Thankfully, using generators and q.async, we can dramatically simplify our code.

You can convert below nested promises chain

function doTasks () {
    // taskA, taskB, and taskC all return promise
    return taskA()
.then(function(resultA){
if (resultA) {
return taskB().then(function(resultB) {
return taskC(resultB).then(function(resultC){
return {resultA, resultB, resultC};
})
});
}
});
}

to more readable code:

function doTasks () {
return q.async(function* (){

let resultA, resultB, resultC;

resultA = yield taskA();

if (resultA) {
resultB = yield taskB();
resultC = yield taskC(resultB);
return {resultA, resultB, resultC}
}
})();
}

The consumer of `doTasks()` is not aware of implementation detail. All he knows is `doTasks()` return a promise.

Also error propagation makes sense. See below example:

var generator = q.async(function* () {
try {
var ten = yield Q.reject(new Error("Rejected!"));
console.log("Should not get here 1");
} catch (exception) {
console.log("Should get here 1");
console.log(exception.message, "should be", "Rejected!");
throw new Error("Threw!");
}
});

generator().then(function () {
console.log("Should not get here 2");
}, function (reason) {
console.log("Should get here 2");
console.log(reason.message, "should be", "Threw!");
});

You might want to use babel to polyfill generators, however it does have overhead in terms of performance.

Below is sample code which uses generators in different node versions. Notice that Node 5 is two times faster than Node 0.12.