Grofit Grofit - 3 months ago 10
Javascript Question

A bit confused with q and promises in nodejs

I currently have a few js files in nodejs which are loaded as module and augment the app object (using express).

So their signatures look like:

module.exports = function(app, callback) {
// ...
callback();
}


So currently as I have about 5 of them my code would look like:

require("./setup/a")(app, function() {
require("./setup/b")(app, function(){
require("./setup/c")(app, function(){
require("./setup/d")(app, function(){
require("./setup/e")(app, function(){
startApp();
})
})
})
})
});


Now that looks unsightly as its the "pyramid of doom", however I am not entirely sure how I need to change this pattern to use Q, as I was assuming I would use
Q.fcall(...a).then(...b).etc.done()
. However I am unsure how I pass the app into it and if I need to return the callback for it to process as a promise.

Ideally I do not want to start whacking Q all through my code I only want it in the places where I want to remove the pyramid use cases, so in the above example how do I use Q with promises to pass the app into each required module and then start the app at the end?

Answer

Assuming your modules don't already use promises you can do something like this:

module.exports = function(app) { 
    // do some stuff with app
    return new Promise(function(resolve,reject){
        // when ready to resolve after some actions on app
        resolve(); // you can also return a value here as a cb param
    });
};

Promise.all(["./setup/a","./setup/b","./setup/c"].map(require.bind(null,app)))
  .then(startApp);

You should however use promises at the lowest level possible, which would mean that you can simply return the promise which you used in the process:

module.exports = function(app){
     return something(app).then(function(){
         return somethingElseAsyncWithApp(app);
     });
};

So the promise constructor isn't required. Note that this answer uses native promises but will also work on libraries that use that syntax like Bluebird. For Q change new Promise to new Q.Promise and Promise.all to Q.all.

Alternatively, you can change every require(x) to Q.fcall(require,x) and use Q.all on that directly, but that is both slow (Although Q is slow anyway) and more error prone than promisifying the modules directly. It is best to promisify the lowest level API possible.