11

Using mongoose to query results from the db and Q for promises, but finding it hard to wrap my head around just getting a list of users that's available. Currently I have some something like this:

var checkForPerson = function( person ) {
    people = mongoose.model('Person', Person)

    return people.findOne({"_id": person }, function(err, doc) {
        if (err) console.log(err)

        if (doc !== null) {
            return doc
        } else { 
            console.log('no results')
        }

    })
}

var promises = someArrayOfIds.map(checkForPerson);

// this is where I would like to have an array of models
var users = Q.all(promises)

//this fires off before the people.findOne query above to users is undefined
SomeOtherFunction( users )

How would I go about having the queries finish before SomeOtherFunction without doing tons of sloppy callbacks?

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504

4 Answers4

20

Another suggestion would be to use MongoDB's $in operator to pass in an array to find and get a large set of results efficiently. Each will be a Mongoose object.

var promise = people.find({ _id: { $in: someArrayOfIds }).exec();
promise.then(function(arrayOfPeople) {
  // array of people ... do what you want here...
});

This would be far more efficient than making multiple requests, one for each _id.

WiredPrairie
  • 58,954
  • 17
  • 116
  • 143
  • 1
    Never knew about that `$in`, I will definitely try this! –  Apr 03 '14 at 13:28
  • 1
    @delboud op if this is the answer that ended up solving your issue you should consider accepting it over mine which you just accepted. In all honesty while my answer gives a lot more theoretical background and useful information about promises IMO - this is the practical answer for people facing this issue in Mongoose. You can upvote my answer if you find it useful but I'm all for the practical solution :) – Benjamin Gruenbaum Apr 03 '14 at 13:30
  • 1
    I was actually struggling with promises so yours answered my question but, he gave me insight on a feature. Either way with me :) –  Apr 03 '14 at 13:32
  • Glad I could help with your problem :) – Benjamin Gruenbaum Apr 03 '14 at 13:34
5

The answer of the question "how do I continue with promises" is almost always with .then. It is the promise analogy of ; and it terminates an asynchronous statement. You can return promises in it and it will unwrap them before continuing.

Q.all(promises).then(function(users){
    SomeOtherFunction(users);
});

Or simply Q.all(promise).then(SomeOtherFunction)

You would also need findOne to actually return promises. You can either use Q.nfcall which calls a node function or promisify it yourself.

What Q.all does is accept an array of promises and fulfills when all of them do and rejects when one of them rejects. You might want to append a .catch handler in case any of the queries fail or use .done in order to signify the end of a chain. Other promise libraries like Bluebird will pick up errors for you even without .done or adding an explicit handler, sadly Q doesn't do this.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 4
    FYI: `findOne().exec()` returns a Promise. – WiredPrairie Apr 01 '14 at 21:23
  • 2
    @WiredPrairie in that case - OP should definitely use `findOne(..).exec` instead of bothering with nfcall or promisifying it manually. OP - also note this means you don't need all the `.if(err)` stuff, promises take care of it for you. – Benjamin Gruenbaum Apr 01 '14 at 21:25
4

You could also use q (npm install q)

var q = require('q')
, aPromise = mongooseModelA.find({_id: aId}).exec()
, bPromise = mongooseModelB.find({_id: bId}).exec();

q.all([aPromise, bPromise]).then(function(bothA_and_B) {
  console.log(bothA_and_B);
});
Brian P Johnson
  • 703
  • 6
  • 17
0

Using Promise API could be an option here:

const arrayFullOfPromises = [];
arrayFullOfPromises.push(mongooseModelA.find({_id: aId}).exec());
arrayFullOfPromises.push(mongooseModelB.find({_id: bId}).exec());
Promise.all(arrayFullOfPromises).then(results => console.log(results));
Przemek Nowicki
  • 572
  • 4
  • 7