2

I just started trying to implement Promises on my Node.js app. Right now i'm checking if a user and password exists then using mongodb to look them up if user isn't found it sets promise.reject() but it is returning the promise too early and it's still in the pending state. If anyone can help or give me ideas on how to refactor it would be much appreciated.

https://gist.github.com/joshbedo/8957056

Hector Correa
  • 26,290
  • 8
  • 57
  • 73
Josh Bedo
  • 3,410
  • 3
  • 21
  • 33
  • 1
    as a side note: use something stronger than md5 hashing for passwords and I wouldn't rely on uuid for the confirmation code either. – mnemosyn Feb 12 '14 at 15:59
  • yeah it's just to get it running plus i didn't want to expose my security module :p – Josh Bedo Feb 12 '14 at 16:15
  • Make sure you return after the first defer.reject(err) otherwise defer.reject() will be called again in defer.reject("User not found") – Hector Correa Feb 12 '14 at 16:17

1 Answers1

5

It is the expected behavior it returns the promise while it is still in the pending state.

You should use then() to check for promise resolution.

Another thing is that you should not use the traditional mongo interface when starting to work with promises, but rather promisify all method of the Collection prototype, so you can return the Promise created by the mongo find method with its chain of thenable. Otherwise I don't see the point of using promises. The way you do does not reduce the amount of code you have to write.

By the way, IMHO you should use bluebird rather than Q, as it is at the moment the only promise library which is not extremely slow.

Example :

in db.js

var mongoClient = require('mongodb').MongoClient;
var collection  = require('mongodb').Collection;

var Promise     = require('bluebird');

// We promisify everything. Bluebird add ***Async function which return promises.
Promise.promisifyAll(collection.prototype);
Promise.promisifyAll(mongoClient);

//In mongodb cursor is not built from protoype I use to promisify it each time. Not necessary if you don't use cursors.
collection.prototype._find = collection.prototype.find;
collection.prototype.find = function() {
  var cursor = this._find.apply(this, arguments);
  cursor.toArrayAsync = Promise.promisify(cursor.toArray, cursor);
 return cursor;
};

//then you connect to the DB and exports your collections...

elsewhere, taking your example :

this.loginUser = function(user) {
  var userid = user.userid,
  var password = (user.password) ?
    crypto.createHash("md5").update(user.password).digest("hex"):
    undefined
  var rememberme = user.rememberme;

  if(userid && password) {
    // we directly return the promise from mongodb, that we chain with a then
    return db.users.findOneAsync({ email: userid, password: password }) // return a promise that we chain
    .then(function(user) { // that we chain
      if(user) {
        var logincode = uuid.v4(),
        var token = jwt.encode({email: userid, password: password}, secret);

        if(rememberme) {
          res.cookie("clogincode", logincode, { magAge: 900000 } );
        }
        return user;  // return in a then callback will just resolve the promise
      } else {
        throw  new Error('User not found'); // throwing in a then callback will just reject the promise
      }
    }); // end of the then, then return a promise that we return (with the return keyword above)
  } else {
    return Promise.reject("Username or Password was not entered"); // this is the only case where we have to actually create a promise ourself
  }
}
Dmitry Minkovsky
  • 36,185
  • 26
  • 116
  • 160
jillro
  • 4,456
  • 2
  • 20
  • 26
  • Do you happen to know what IcedCoffeeScript's await and defer uses? yeah I want it to check if variables are set, then query database, then check if a result is fulfilled or rejected. It seems like I'm adding a lot more to my code base trying to achieve that functionality. – Josh Bedo Feb 12 '14 at 16:17
  • do you have a preference on library for mongo like promisify-mongo – Josh Bedo Feb 12 '14 at 16:54
  • I personnaly use bluebird's promisify method which basically can promisify any library using node style callbacks. I dont know if Q has the same kind of utility fonctions. – jillro Feb 12 '14 at 17:04
  • Do you have any examples you can show? – Josh Bedo Feb 12 '14 at 17:16
  • 3
    I am on my phone now, but I will show you when I am home :) – jillro Feb 12 '14 at 17:18
  • For future visitors, also see http://stackoverflow.com/q/23771853/438992. – Dave Newton Aug 13 '14 at 22:35