2

I have following nodejs code:

app.get('/dashboard', function(req, res){

    db.collection('com_url_mamy').find().toArray(function(err, doc){
      db.collection('com_url_mamy').find({'price':''}).count(function(err, docs){
        db.collection('com_url_mamy').find({"price":{$not:{$ne:"last_price_1"}}}).count(function(err, last_doc){

        if(err){console.log(err);}
        console.log(docs);
      res.render('dashboard',{'doc':doc, 'docs':docs, 'last_doc':last_doc});
    });
    });
  });

Here i have to add more two or three query/callback.

But I don't think this is right way to do. Please any one can me tell me how I can solve this problem to increase performance.

Thank You

YAM
  • 474
  • 2
  • 7
  • 14
  • 2
    @MehdiElFadil This is not a duplicate. He can improves a lot his code using Promises and multiple concurrent async calls. The question you linked is about code style while his question is about performance issue in a particular scenario than can be improved. – Jonathan Muller Mar 05 '16 at 12:23
  • 1
    I pasted my answer here since question has been closed for no reason http://paste.ofcode.org/zHmnq2u8qKzDx9ggnYHep9 Since your 3 queries are not dependent on each others, you can use ```Promise.all``` to execute the 3 of them in parallel. – Jonathan Muller Mar 05 '16 at 12:27
  • 2
    BTW ```{"price":{$not:{$ne:"last_price_1"}}}``` can be refactored to ```{"price": "last_price_1"}``` (not not equal is equal :p) – Jonathan Muller Mar 05 '16 at 12:30

2 Answers2

4

Using async/await will be an appropriate solution that avoids the callback hell for you in this case. Consider running your queries as follows:

app.get('/user/:name', async (req, res, next) => {
    try {
        const docs = await db.collection('com_url_mamy').find().toArray()
        const count = await db.collection('com_url_mamy').find({'price':''}).count()
        const last_doc = await db.collection('com_url_mamy').find({"price": "last_price_1"}).count()

        res.render('dashboard', { docs, count, last_doc })
    } catch (err) {
        return next(err)
    }
}

As an alternative, you can use the async libary especially the async.parallel() method when you need to run multiple tasks that do not depend on each other and when they all finish do something else.

Consider the following example:

app.get('/user/:name', function(req, res, next) {
    var locals = {};
    async.parallel([

        // Load all documents 
        function(callback) {
            db.collection('com_url_mamy').find().toArray(function(err, docs){
                if (err) return callback(err);
                locals.docs = docs;
                callback();
            });
        },

        // Get count of documents where price is empty 
        function(callback) {
            db.collection('com_url_mamy').find({'price':''}).count(function(err, count){
                if (err) return callback(err);
                locals.count = count;
                callback();
            });
        },

        // Load last docs 
        function(callback) {
            db.collection('com_url_mamy').find({"price": "last_price_1"}).count(function(err, docs){
                if (err) return callback(err);
                locals.last_doc = docs;
                callback();
            });
        }
    ], function(err) { //This function gets called after the three tasks have called their "task callbacks"
        if (err) return next(err);
        //Here render dashboard with locals object
        res.render('dashboard', locals);
    });
});
chridam
  • 100,957
  • 23
  • 236
  • 235
3

You can use native Promises with MongoDB driver (on node.js >= 0.12):

app.get('/dashboard', function(req, res){
  var proms = [];

  proms.push(db.collection('com_url_mamy').find().toArray());
  proms.push(db.collection('com_url_mamy').find({'price':''}).count());
  proms.push(db.collection('com_url_mamy').find({"price": "last_price_1"}).count());

  Promise.all(proms)
  .then(function(pres) {
    res.render('dashboard',{'doc':pres[0], 'docs': pres[1], 'last_doc': pres[2]});
  })
  .catch(console.error);
});

Promise.all takes the promises you give it and execute them in parallel.

The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

(BTW I think you should rename your question with something more like 'Improve independent nested async calls' to avoid the closing/duplicate issue you had.)

Jonathan Muller
  • 7,348
  • 2
  • 23
  • 31