8

I have a simple Node module that exports a function that makes a database query. The problem is that this function returns before the database query completes.

"use strict";

var mongoose = require('mongoose'),
    Model    = require('./entities/user/model'),
    _        = require('underscore');

let dao = new Model(mongoose);

module.exports.isAdmin = function(user_id) {
  var params = {'roles': 'admin'};

  dao.findOne(params, function(err, user) {
    if (err) {
      logger.error(err);
      return false;
    }
    if (_.indexOf(user.roles, 'admin') != -1) {
      logger.info("admin user: " + user._id);
      if (user._id == user_id) return true;
    }
    return false;
  });
};

isAdmin function searches the users collection in order to find out if user_id belongs to admin user.

The problem is that isAdmin function doesn't wait for the response of findOne query.

How could I force the isAdmin function to return only when query returns results ?

Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
Matko
  • 3,386
  • 4
  • 21
  • 35
  • Pass in a callback to `isAdmin` or you can do `return dao.findOne(params)` since it should return a promise you can chain to – Explosion Pills Feb 26 '16 at 21:40
  • 2
    You seem to be missing a lot of concepts here. For starters, once you register a `Model` with mongoose it persists for the lifecycle of the application, so you don't need to keep passing in the `mongoose` instance. Just call `mongoose.model("ModelName")` or do a `require(..)` to the appropriate module where there was a `module.exports` on that previous definition if you prefer. Secondly you are asking about promises but not using them. If you want `.findOne()` to return a promise then issue an `.exec()` like the documentation says. Then of course your function should itself return a promise – Blakes Seven Feb 26 '16 at 22:47

1 Answers1

13

Because findOne is async function, one way to return from it is through callback function

module.exports.isAdmin = function(user_id, callback) {
  var params = {'roles': 'admin'};

  dao.findOne(params, function(err, user) {
    if (err) {
      logger.error(err);
      callback && callback(false);
    }
    if (_.indexOf(user.roles, 'admin') != -1) {
      logger.info("admin user: " + user._id);
      if (user._id == user_id)
          callback && callback(true);
    }
    callback && callback(true);
  });
};

isAdmin(userId, function(v) {
    console.log(v);
})

Another way is to get Promise in findOne, as this doc said, .exec() gives you a fully-fledged promise. Even with Promise, to meet you requirement, the result could be returned through callback function.

module.exports.isAdmin = function(user_id, callback) {
  var params = {'roles': 'admin'};

  var queryPromise = dao.findOne(params).exec();
  queryPromise.then(function(user) {
      if (_.indexOf(user.roles, 'admin') != -1) {
        logger.info("admin user: " + user._id);
        if (user._id == user_id)
          callback && callback(true);
      }
    }, function(err) {
      callback && callback(false);
  });
};
zangw
  • 43,869
  • 19
  • 177
  • 214
  • 2
    Can you elaborate on what a 'fully fledged promise' is? – Harley Alexander Oct 10 '16 at 22:38
  • 2
    @HarleyAlexander, `fully fledged promise` means same function of [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) as it defined, but Mongoose queries are not promises by default. Even though they do have a `.then()` function. If you need a fully-fledged promise, use the `.exec()` with each queries. This is something to consider when you are using features like bluebird’s `reflect()` function. You should return a full fledged promise to use this feature. – zangw Jul 24 '17 at 07:57