Moshe's Blog

Tips and tricks learnt along the way.

Require a Promise

| Comments

I was recently asked how to make a node module that needs to do a database lookup or something else before it can give you an API. The main problem is that require is synchronize and doesn’t have a concept of callback or a mechanism to wait for a response The are a couple of ways to solve this, I’ll first go through the standard solution and then how I would do it.

The standard way people solve this is by having the API take a callback:

var gister = require('gister');

gister(function(err, create) {
    if (err) throw err;
    create(fileContents);
});

Now this is easy to read and makes a lot of sense, but you run into a problem of when you have more than one module like this:

var gister = require('gister');
var snippeter = require('snippeter');
var apis = {};
var ticks = 2;

gister(function(err, create) {
    if (err) throw err;
    apis.gister = create;
    if (--ticks === 0) ready(apis);
});
snippeter(function(err, create) {
    if (err) throw err;
    apis.snippeter = create;
    if (--ticks === 0) ready(apis);
});
function ready(apis) {
    // apis.gister
    // apis.snippeter
}

This definitly doesn’t scale, fortunately we can solve this using the magic of Promises

File index.js:

var Promise = require("bluebird");

var soon = require('./soon.js');

Promise.all([soon]).then(function(soon) {
    console.log(soon);
});

File soon.js

var Promise = require("bluebird");

var ret = {
    obj: 'to return'
};

module.exports = new Promise(function(resolve, reject) {
    setTimeout(function() {
        if (Math.random() < .1) {
            console.log('about to reject');
            reject(new Error('Db error'));
            console.log('rejected');
        } else {
            console.log('about to resolve');
            resolve(ret);
            console.log('resolved');
        }
    }, 5000);
});

//module.exports = ret;

Now the cool thing about doing it this way is that we can switch back to a synchronize way of loading without having to change any code. We can simulate this by uncommenting the last line //module.exports = ret; and it will still run the same

Comments