0

I use the MongoJS driver to run queries in my Nodejs server. I have two queries that I would like to run in order, but the asynchronous nature of javascript leaves me with unexpected results when I make a request. Here's what I have:

app.post('/teams', isLoggedIn, function (req, res){

    db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$pull:{"teams":{"_id":req.body.y}}},
    function (err, docs) {
    console.log(docs); res.json(docs);
    });

    db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$push:{"teams":{"_id":req.body.y,"team":req.body.z, "member":true}}},
    function (err, docs) {
    console.log(docs); res.json(docs);
    });

});

I need these queries to run in order ($pull and THEN $push). I've tried running the second query within the function(err, docs){...} of the first query, but even when I pass req or variablized versions of req, it returns as undefined. What's the best way to go about getting these queries to run in order?

Update: Here's what I ended up doing using async (Thanks for the tip, Satisfaction) based on this tutorial. Something I should have mentioned was that this query exists within a loop, which I did not include in my example request above.

async.parallel([
       function(callback) { //This is the first task, and callback is its callback task
          console.log('first1 '+x+'pulled');
          db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$pull:{"teams":{"_id":req.body.y}}}, function(err, docs) {
          //Now we have saved to the DB, so let's tell async that this task is done
          res.json(docs);
          callback();
        });
       },
       function(callback) { //This is the second task, and callback is its callback task
          console.log('second1 '+x+'pushed');
          db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$push:{"teams":{"_id":req.body.y,"team":req.body.z, "member":true}}}, callback); //Since we don't do anything interesting in db.save()'s callback, we might as well just pass in the task callback 
          }
          ], function(err, docs) { //This is the final callback
          res.json(docs);
          console.log('Both a and b are saved now');
});

Update 2: Maybe I spoke too soon. Queries are still not completing as expected about 5% of the time.

2 Answers 2

2

As you mentioned yourself, NodeJS works asynchronously.

To get stuff done in sync, you'll need to use a library, like, async or use promises.

Just a quick example how you could do things work using async's "auto" (I like using it as I have much more control over the simplistic "waterfall" method):

async.auto({
    pull: function(callback){
        db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$pull:{"teams":{"_id":req.body.y}}},callback);
    },
    push: ['pull', function(callback, results){
        db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$push:{"teams":{"_id":req.body.y,"team":req.body.z, "member":true}}},callback);
    }]
}, function(err, results) {
    if(err) {
        console.log(err);
    }
    console.log(results.pull);
    console.log(results.push);
});

I have not tested the code, feel free to look it all up yourself here.

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

11 Comments

That doesn't make things happen in sync, it just makes it READ like sync.
Well, you could say that it happens in sync since 'push' always happens after 'pull'? As far as the user needs to know, it all happens in sync, all of the async stuff is encapsulated and we don't need to worry about it.
Wouldn't the async.parallel() method be more appropriate in this case? Since the docs state, "parallel is about kicking-off I/O tasks in parallel, not about parallel execution of code. If your tasks do not use any timers or perform any I/O, they will actually be executed in series. Any synchronous setup sections for each task will happen one after the other. JavaScript remains single-threaded."
@Satisfaction it makes those tasks run sequentially, but it doesn't block code coming after.
@chridam, I am not 100% sure but parallel starts and returns the results in order of the array. That means that if the first query is slower, it might still end after the second one is done. .series() would be an easier way of doing what he needs but is a bit less powerful.
|
1
var Q = require("q");

app.post('/teams', isLoggedIn, function (req, res){

firstFunction(req,res).then(function(result){
    var resultFromFirst = result
    db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$push:        {"teams":{"_id":req.body.y,"team":req.body.z, "member":true}}},
        function (err, docs) {
            console.log(docs); res.json(docs);
        });

})


});

var firstFunction = function (req,res) {
console.log("first function")
var d1 = Q.defer();
  db.collection.update({"_id":mongojs.ObjectId(req.body.x)},{$pull:    {"teams":{"_id":req.body.y}}},
    function (err, docs) {
        console.log(docs); res.json(docs);
        d1.resolve(docs);
    });


return d1.promise;
}

Before Run:

npm install q -g

use sudo if any issue in package installation

1 Comment

What if I'm running my two queries within a for loop? The initial $pull works fine, but when I go to the $push, the iterator defined by my loop is undefined.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.