Reputation: 7057
I have a mongob with user object. Here's the schema:
{
"_id": {
"$oid": "50658c835b821298d3000001"
},
"email": "admin",
"password": "admin",
"id": 1
}
I wrote a simple demo (only main parts):
function findByEmail(email, callback) {
db.collection("users", function(err, collection) {
collection.find({}, function(err, users) {
users.each(function(err, user) {
if (user) {
if (user.email === email) {
//for a case, is we found user - return it
callback(null, user);
}
}
});
//for a case, is we didn't find user - return null
callback(null, null);
});
});
}
And a route for testing:
app.get('/test',function(req,res){
findByEmail("admin", function(err, user){
res.send(user);
})
})
After lauching localhost:3000/test I get
Error: Can't set headers after they are sent.
If I comment line callback(null, null);
I dont get this error. It seems that callback works... twice! How can it be? I supposed that if I if (user.email === email) { ... }
works, and callback(null, user);
is launched, function findbyEmail
returns <user>
to app.get
, but callback works twice (for callback(null, null);
also) even when if (user.email === email) { ... }
is true.
Upvotes: 0
Views: 608
Reputation: 43208
Let's refer to the function you pass to collection.find
as outer
and to the one you pass to users.each
as inner
(I have labeled them so in the code block below for clarity). Now, the problem is that when you invoke callback(null, user)
in inner
, the users.each
loop continues to run, and will still proceed with testing all the remaining elements in the result; also, the outer
function is still not finished, and will invoke your callback(null, null)
callback after the users.each
loop is done. You can't really just return
after you find a user and invoke callback(null, user)
as I previously suggested, as this will only terminate that single iteration of the users.each
loop and will proceed with the next and all of the remaining elements; you need to define a flag indicating whether you have found the record already or not:
collection.find({}, function outer(err, users) {
var found = false;
users.each(function inner(err, user) {
if (user && !found) {
if (user.email === email) {
//for a case, is we found user - return it
callback(null, user);
found = true;
}
}
});
//for a case, is we didn't find user - return null
if (!found) {
callback(null, null);
}
});
Also note that you can do that search for a user record by email entirely in mongo and replace your whole logic with this:
collection.find({ email: email }, function(err, users) {
callback(null, users.length? users[0]: null);
});
Upvotes: 0
Reputation: 75854
You callback still gets executed:
for ( var key in obj ) {
foo();
}
foo();
Why would you expect the 2nd foo() not to be called?
You need to do return foo(); if you don't want to keep going.
Upvotes: 0
Reputation: 25938
You are calling callback(null, null)
right after you iterate over results. So if you have some results - callback
will be called twice:
You should only call callback(null, null)
if there is no results...
if (!users || users.toArray().length==0) callback(null, null);
Upvotes: 3