Why could the example below be returning the node middle-ware function before my mongoose db lookup function finishes? I am sure this is a async issue but I am a bit lost on why.
Middle-ware js file
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function isUser(login){
var UsersSchema = new Schema({
user: String,
user_type: String,
password: String,
first_name: String,
middle_name:String,
last_name: String,
birth_date: Date,
join_date: Date
});
var UserModel = mongoose.model('users', UsersSchema);
mongoose.connect('mongodb://localhost/identity');
mongoose.model('users', UsersSchema);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error: '));
db.once('open', function cb () {
UserModel.findOne({'user': login}, function (err, user){
if (err){ throw err; }
console.log('connected to user db and preformed lookup.');
console.log(user);
return user;
});
});
}
module.exports.authenticate = function (login, password, cb) {
var user = isUser(login),
msg;
console.log(user);
if (!user) {
msg = 'Invalid User';
cb(null, msg);
return;
}
if (user.password != password) {
msg = 'Invalid Password';
cb(null, msg);
return;
}
cb(user, null);
};
Console Output
undefined
Login Failed! : Invalid User
connected to user db and preformed lookup.
{ _id: 51c8e16ce295c5b71ac6b229,
user: 'bclark@themindspot.com',
user_type: 'admin_master',
password: 'enter',
first_name: 'Brandon',
middle_name: 'Laurence',
last_name: 'Clark',
birth_date: Fri Mar 19 1982 00:00:00 GMT-0800 (PDT),
join_date: Wed Jun 26 2013 00:00:00 GMT-0700 (PDT) }
db.once and UserModel.findOne are asynchronous functions, hence why you provide an anonymous function that is called when they are done. If you want your isUser function to 'return' the results of these asynchronous functions you will have to make it use a callback as well.
Replace
function isUser(login){
with
function isUser(login, callback){
and
return user;
with
callback(user).
It is also recommended to not throw errors in asynchronous code, but pass them on with the callback, similar to what db.once and UserModel.find do, like so:
Remove
if (err){ throw err; }
and replace the callback above with
callback(err, user);
While you're at it, since you don't do anything with the error or user anymore, you might as well call UserModel.findOne({'user': login}, callback);
===
The complete thing would become the following. Note that I follow the callback(err, result) convention.
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function isUser(login, callback){
var UsersSchema = new Schema({
user: String,
user_type: String,
password: String,
first_name: String,
middle_name:String,
last_name: String,
birth_date: Date,
join_date: Date
});
var UserModel = mongoose.model('users', UsersSchema);
mongoose.connect('mongodb://localhost/identity');
mongoose.model('users', UsersSchema);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error: '));
db.once('open', function cb () {
UserModel.findOne({'user': login}, callback);
});
}
module.exports.authenticate = function (login, password, cb) {
var user = isUser(login, function(err, user) {
if (err) {
cb(err);
}
console.log(user);
if (!user) {
msg = 'Invalid User';
cb(msg);
return;
}
if (user.password != password) {
msg = 'Invalid Password';
cb(msg);
return;
}
cb(null, user);
});
};
Finally, consider using (custom) Error objects instead of String messages, google it for explanations as to why.
yes this is a async issue. you may know that node.js every action has a separate thread
in your code you call
var user = isUser(login),
according to You it should return the result from the isUser function but the execution give this function a separate thread and it goes on to the next statement.
as on the next statement the user is undefined because it doesn't return anything from the function isUser
so the statement if (!user) { becomes true
to avoid this error you should place isUser in a callback function
means restrict the execution until and unless you get response from the function