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