-1

I have a User model function all that returns all users in an array.

User = {
  all: function() {
    var users = [];

    globalLibrary.db.collection('users').find({}).toArray(function(err, items) {
      test.equal(null, err);
      users = items;
    });

    return users;
  }
}

I want to ensure that the function doesn't finish or return before the mongodb query is complete.

Currently, this function is just returning [], and querying mongodb asynchronously. I want the function to wait until the function finish query is complete and it returns and array filled with users.

Note:

globalLibrary.db is just a cached mongodb connection.


My solution using promises

Since some people closed the question as a duplicate, I'll write my answer here within the question. Hopefully someone else who is not familiar with asynchronous programming find this useful.

Problem

The point is that you need to use a callback - there's no way to block on something asynchronous. (...) – Aaron Dufour

The function User.all() I wrote above will return empty array because nothing is stopping the process while the mongodb query is happening. There are some primitive ways to stop the process.

You can crank up some hacky stuff using setTimeout(). This way sucks though because you have to use some arbitrary time that might be higher than the actual time you need to query the mongodb. Simply put, it's slower.

You can also use some event based stuff that @AaronDufour linked in the comment (now deleted). So you can have something a pair of event emitter and a listener to replace setTimeout() way. @Someone points out though that you shouldn't use this in node.js for blocking function.

Now finally, the conventional way of dealing with this problem is using callbacks as pointed out by the answer below. This is fine, but callbacks can quickly get out of control once you start having multiple callbacks stacked inside one another.

I am using https://github.com/kriskowal/q for promises. While using promises doesn't solve all the woes of callbacks, but it looks most like simple synchronous programming style which I think is a huge plus.

First do npm install --save q to start using Q package.

Here's my new User.all() function.

var Q = require('q')

var Users = {
  all: function() {
    var deferred = Q.defer();

    globalLibrary.db.collection('users').find({}).toArray(function(err, items) {

      if (err) {
        deferred.reject(new Error(err));
      } else {
        deferred.resolve(items);
      }
    });

    return deferred.promise;
  }
}

Now if you want to use User.all().

User.all()
.then(function (docs) {
  // docs is the array of returned users from mongodb query.
  console.log(docs);

}, function(error) {
  // do something if there's an error.
}, function(progress) {
  // do something while the query is running.
});
Community
  • 1
  • 1
Jason Kim
  • 18,102
  • 13
  • 66
  • 105
  • 1
    You need to pass a callback to the function which will be executed when the query finishes. – Vsevolod Goloviznin Jan 20 '15 at 19:05
  • 2
    Don't use an event based modern programming environment (JS/node.js) if you want blocking IO. one of the major points of them is to NOT do what you ask for. – Mörre Jan 20 '15 at 19:13
  • @AaronDufour, the linked question makes use of `node-metainspector` package. This question does not. I did enough research on SO and Google and I could not find anything similar to my question. Please unflag my question for more views. – Jason Kim Jan 20 '15 at 19:17
  • 1
    @Twitterhandlejasoki The point is that you need to use a callback - there's no way to block on something asynchronous. The exact asynchronous function that you're using is irrelevant. – Aaron Dufour Jan 20 '15 at 19:18
  • @AaronDufour, I added my answer in my question for now. I still think it's a valuable question that merits an answer. – Jason Kim Jan 20 '15 at 22:00
  • 1
    @Twitterhandlejasoki I agree that it has value, but the question "how do I block on this async function" gets asked approximately once a day here, and closing as dupe is the only sane way to handle that. The dupe suggested by JohnnyHK is better than the one suggested by me - it's something of a canonical "async? what?" question for node.js. – Aaron Dufour Jan 20 '15 at 23:01

1 Answers1

2

The preferred way of doing this in node.js is to embrace the async nature of it and pass in a callback to the function. A few decent tutorials (last one including mongodb examples):

http://justinklemm.com/node-js-async-tutorial/

http://msdn.microsoft.com/en-us/magazine/dn754378.aspx

If you feel you must go against the grain and go synchronous, I'd take a look at this sync library for node.js and mongodb:

https://www.npmjs.com/package/mongo-sync

John Petrone
  • 26,943
  • 6
  • 63
  • 68