Begoodpy
Begoodpy

Reputation: 1084

asynchronous javascript best practices

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 ?

Upvotes: 1

Views: 515

Answers (3)

Jamis Charles
Jamis Charles

Reputation: 6039

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

Upvotes: 0

bevacqua
bevacqua

Reputation: 48476

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.

Upvotes: 1

Andrew Whitaker
Andrew Whitaker

Reputation: 126052

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);
};

Upvotes: 0

Related Questions