1

Using node.js/express.js, here is an example of a typical problem that I face with asynchronous code (function rendering a page on a request with 2 db call):

exports.index = function(req, res){
    res.render('index', {
        data1: getData1(),
        data2: getData2()
    });
};

Here is what one function could look like:

function getData1() {
    var assets = db.get("assets");
    assets.find().on('success', function (docs) {
        // What to do ?
    });
}

I tried using async.js which is a very helpful module, but I'm still missing knowledge on how to do with a case like this.

And can someone suggest good resources for best practice regarding asynchronous javascript coding ?

1

3 Answers 3

1

Use an asynchronous workflow library, such as async.

function getData1(done) {
    var assets = db.get('assets');
    assets.find().on('success', done);
}

function getData2(done) {
    var assets2 = db.get('assets2');
    assets2.find().on('success', done);
}

exports.index = function(req, res, next){
    async.parallel({
        d1: getData1,
        d2: getData2
    }, render);

    function render (err, data) {
        if (err) {
            return next(err);
        }

        res.render('index', {
            data1: data.d1,
            data2: data.d2
        });
    });
};

In this way, you can stick to the callback(err, result) style perpetrated throughout Node packages, while keeping your code sane and pretty. async provides methods to simplify your workflows. In this case, I used the async.parallel method which allows you to run any amount of callbacks concurrently, and then runs the function provided as a second argument when the other callbacks is done.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Nico, I like the approach with async. Do it means that in getData I have to call assets.find().on('success', done(err, queyrresult)); ? If so, it doesn't make the functions getData really reusable, right ?
You can just pass the done callback, as in .on('success', done), because it has the (err, result) signature you describe. They're completely reusable because you can use those same queries in other contexts where you need to do getData1 or 2
0

One way to solve your problem would be to chain the asynchronous commands by passing a callback function to the functions performing the asynchronous operations:

exports.index = function(req, res){
    getData1(function (data1) {
        getData2(function (data2) {
            res.render('index', {
                data1: data1,
                data2: data2
            });
        });
    });
};

function getData1(callback) {
    var assets = db.get("assets");
    assets.find().on('success', callback);
}
// Similarly for getData2

However, the nested callbacks can become unmanageable quickly. You can name the functions to help with this:

exports.index = function(req, res){
    var data2Success = function (data1, data2) {
        res.render('index', {
            data1: data1,
            data2: data2
        });
    },
    data1Success = function (data1) {
        getData2(data2Success.bind(this, data1));
    };

    getData1(data1Success);
};

Comments

0

I usually do one of the following:

1) Nest your calls:

exports.index = function(req, res){
  //first call
  assets.getRecord(function (err, docs) {

    // 2nd call
    assets.getSomeOtherRecord(function (err, otherDocs) {
        res.render('index', {
          data1:docs,
          data2: otherDocs
        });
    );

  });

};

If your code is simple, and you don't mind running these one after the other, then this suffices. If those don't apply, I use approach #2:

2) Use async.js or Q as the other answers here show.

Resources for learning about async:
- http://net.tutsplus.com/tutorials/javascript-ajax/managing-the-asynchronous-nature-of-node-js/
- http://www.sebastianseilund.com/nodejs-async-in-practice

1 Comment

Thanks Jamis, 1) is definitely not a way that I want to go with as my two db query will not be reusable at all. Thanks for the resources!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.