Loop skipping over a record in Mongo database - javascript

I've come across a very peculiar bug.
I have a database with the following records:
{
"_id": {
"$oid": "xxxxxxxxxxxxxxxxx"
},
"name": "Name1",
"number": 42
}
{
"_id": {
"$oid": "xxxxxxxxxxxxxxxxx"
},
"name": "Name2",
"number": 123
}
In my javascript code, I have the following code:
MyRecords.find({}, function(err, results) {
if (err) {
console.log("Error finding phone recipients:", err);
} else if (results.length == 0) {
console.log("No users");
} else {
for (var i = 0; i < results.length; i++) {
var result = results[i];
someFunction2("", result);
someFunction1(function(message) {
someFunction2(message, result);
});
result.number = "Some new value"
result.save(function(err) {
if (err) {
console.log("Error updating last sent for", result.name);
} else {
console.log("Updated last sent time for user:", result.name);
}
});
}
}
}
Assume that someFunction1 and someFunction2 make some API calls and wait for results for a few milliseconds.
When I run this, the console prints out:
Updated last sent time for user: Name2
The user Name1 is not processed. What is going on here?

Assume that someFunction1 and someFunction2 make some API calls and wait for results for a few milliseconds.
Hi, and there is exactly your problem. Once someFunction2 and someFunction1 have retrieved the data from the API, the loop has already run and the value of 'result' will be the last one in the loop.
A way to avoid this is to abstract those to a function outside of the loop, which receives 'result' as a parameter like so:
MyRecords.find({}, function(err, results) {
function processResult(result) {
someFunction2("", result);
someFunction1(function(message) {
someFunction2(message, result);
});
result.number = "Some new value"
result.save(function(err) {
if (err) {
console.log("Error updating last sent for", result.name);
} else {
console.log("Updated last sent time for user:", result.name);
}
});
}
if (err) {
console.log("Error finding phone recipients:", err);
} else if (results.length == 0) {
console.log("No users");
} else {
for (var i = 0; i < results.length; i++) {
processResult(results[i]);
}
}
}
Hope it helps

Related

Using async.js for deep populating sails.js

I have a big issue with my function in sails.js (v12). I'm trying to get all userDetail using async (v2.3) for deep populating my user info:
UserController.js:
userDetail: function (req, res) {
var currentUserID = authToken.getUserIDFromToken(req);
async.auto({
//Find the User
user: function (cb) {
User
.findOne({ id: req.params.id })
.populate('userFollowing')
.populate('userFollower')
.populate('trips', { sort: 'createdAt DESC' })
.exec(function (err, foundedUser) {
if (err) {
return res.negotiate(err);
}
if (!foundedUser) {
return res.badRequest();
}
// console.log('foundedUser :', foundedUser);
cb(null, foundedUser);
});
},
//Find me
me: function (cb) {
User
.findOne({ id: currentUserID })
.populate('myLikedTrips')
.populate('userFollowing')
.exec(function (err, user) {
var likedTripIDs = _.pluck(user.myLikedTrips, 'id');
var followingUserIDs = _.pluck(user.userFollowing, 'id');
cb(null, { likedTripIDs, followingUserIDs });
});
},
populatedTrip: ['user', function (results, cb) {
Trip.find({ id: _.pluck(results.user.trips, 'id') })
.populate('comments')
.populate('likes')
.exec(function (err, tripsResults) {
if (err) {
return res.negotiate(err);
}
if (!tripsResults) {
return res.badRequest();
}
cb(null, _.indexBy(tripsResults, 'id'));
});
}],
isLiked: ['populatedTrip', 'me', 'user', function (results, cb) {
var me = results.me;
async.map(results.user.trips, function (trip, callback) {
trip = results.populatedTrip[trip.id];
if (_.contains(me.likedTripIDs, trip.id)) {
trip.hasLiked = true;
} else {
trip.hasLiked = false;
}
callback(null, trip);
}, function (err, isLikedTrip) {
if (err) {
return res.negotiate(err);
}
cb(null, isLikedTrip);
});
}]
},
function finish(err, data) {
if (err) {
console.log('err = ', err);
return res.serverError(err);
}
var userFinal = data.user;
//userFinal.trips = data.isLiked;
userFinal.trips = "test";
return res.json(userFinal);
}
);
},
I tried almost everthing to get this fix but nothing is working...
I am able to get my array of trips(data.isLiked) but I couldn't get my userFInal trips.
I try to set string value on the userFinal.trips:
JSON response
{
"trips": [], // <-- my pb is here !!
"userFollower": [
{
"user": "5777fce1eeef472a1d69bafb",
"follower": "57e44a8997974abc646b29ca",
"id": "57efa5cf605b94666aca0f11"
}
],
"userFollowing": [
{
"user": "57e44a8997974abc646b29ca",
"follower": "5777fce1eeef472a1d69bafb",
"id": "5882099b9c0c9543706d74f6"
}
],
"email": "test2#test.com",
"userName": "dany",
"isPrivate": false,
"bio": "Hello",
"id": "5777fce1eeef472a1d69bafb"
}
Question
How should I do to get my array of trips (isLiked) paste to my user trips array?
Why my results is not what I'm expecting to have?
Thank you for your answers.
Use .toJSON() before overwriting any association in model.
Otherwise default toJSON implementation overrides any changes made to model associated data.
var userFinal = data.user.toJSON(); // Use of toJSON
userFinal.trips = data.isLiked;
return res.json(userFinal);
On another note, use JS .map or _.map in place of async.map as there is not asynchronous operation in inside function. Otherwise you may face RangeError: Maximum call stack size exceeded issue.
Also, it might be better to return any response from final callback only. (Remove res.negotiate, res.badRequest from async.auto's first argument). It allows to make response method terminal

How to return callback on Collection.Insert in mongodb

I am trying to insert the bulk data into the collection. Inserting into the collection is happening properly. but i want the return callback from the Oninsert function. Can anybody help me to return the callback.here is my code.
var Invitation = require('../models/invitation');
var uniqueinvitations=[ { email: 'tyu#gmail.com', role: 'Developer' },
{ email: 'rty#mailinator.com', role: 'Developer' } ]
Invitation.collection.insert(uniqueinvitations, onInsert);
function onInsert(err, docs) {
if (err) {
console.log("Error while inserting the data into the Invitation");
} else {
//i want to return the Callback here,
var invitations = _.map(docs, '_id');
}
}
Something like this should work and display id or inserted rows
function onInsert(err, docs) {
if (err) {
console.log("Error while inserting the data into the Invitation");
} else {
var invitations = docs.map(x => x.id);
console.log("inserted : " + invitations);
}
}

Wait for Meteor.call result on Client

I am new to JavaScript.I am not understanding how to wait for a result of an Meteor.call method.
This is my code
//client/main.js
//Added the callback
Template.hello.events({
'click button'(event, instance) {
// increment the counter when button is clicked
instance.counter.set(instance.counter.get() + 1);
var res = Meteor.call("callMeLater","sanj",function (err,res) {
if (err) {
console.log(err);
} else {
console.log("this is the result main ", res);
}
});
console.log("this is the result ", res);
}
//server/main.js
Meteor.methods({
callMeLater :function (name) {
var callMeLaterSync =Meteor.wrapAsync(callMeLaterAsync);
var result = callMeLaterSync(name);
console.log("this is the test", result);
return result;
}
});
var callMeLaterAsync = function (name,cb) {
setTimeout(function () {
cb && cb (null ,"hey there, "+name);
},2000);
};
On the console, i get
this is the result undefined
this is the result main hey there, sanj
How do i wait for the result of Meteor.call by blocking the execution at the client.
Please help
Thanks
Just put your code into a callback method.
Meteor.call('callMeLater',"sanj", function(err, res){
if (err) {
console.log(err);
} else {
console.log("this is the result ", res);
}
});

Can't Set Headers After they are sent - NodeJS

I have a node js app and one of the routes I keep getting "Can't set headers after they are sent error".
What the route does:
Users in my app have certain access levels so this route goes through the users accessLevel array and finds the appropriate access level for this route. And based on the access level of the user who's calling the route has it performs different actions.
The Code:
app.post('/bios/approve', isLoggedIn, function(req, res) {
for (var i = 0; i < req.user.accessLevel.length; i++) {
if (req.user.accessLevel[i] === "Bio Officer") {
Bio.findOneAndUpdate({userID: req.body.userID, bioForSector: req.body.bioForSector}, {
background: req.body.background,
experience: req.body.experience,
skills: req.body.skills,
bioStatus: req.body.bioStatus
}, function(err, editedBio) {
if (err)
console.log("Error while editing Pending Bio is " + err);
else if (editedBio) {
User.findOneAndUpdate({accessLevel: "Bio Designer"}, {
$push: {biosPending: editedBio._id}
}, function(err, user) {
if (err) {
console.log("The error while finding lineManager is " + err);
} else if (user) {User.findOneAndUpdate({accessLevel: "Bio Officer"}, {
$pull: {
biosPending: editedBio._id
}
}, function(err, bioOfficer) {
if (err) {
console.log("The error while finding lineManager is " + err);
}
res.json("Bio Done!")
});
}
});
}
});
} else if (req.user.accessLevel[i] === "Bio Designer") {
// Currently Empty
} else {
Bio.findOneAndUpdate({userID: req.body.userID,bioForSector: req.body.bioForSector}, {
background: req.body.background,
experience: req.body.experience,
skills: req.body.skills,
bioStatus: req.body.bioStatus
}, function(err, editedBio) {
if (err)
console.log("Error while editing Pending Bio is " + err);
else if (editedBio) {
User.findOneAndUpdate({accessLevel: "Bio Officer"}, {$push: {biosPending: editedBio._id}
}, function(err, user) {
if (err) {
console.log("The error while finding lineManager is " + err);
} else if (user) {
User.findOneAndUpdate({email: editedBio.lineManagerEmail}, {$pull: {biosPending: editedBio._id}
}, function(err, bioOfficer) {
if (err) {
console.log("The error while finding lineManager is " + err);
}
res.json("bio Done!")
});
}
});
}
});
}
}
});
Any help will be greatly appreciated. Does anyone know what am I doing wrong?
Can't Set Headers After they are sent
means you are sending response multiple times for a single request.
From you code what i can suggest is:
for (var i = 0; i < req.user.accessLevel.length; i++) {
if(--req.user.accessLevel.length == 0){
res.json("Bio Done!")
}
}
First try add res.End(); after res.json().
If that doesn't work can you please add the code of 'isLoggedIn'?
Every time you send back a response you should use the return word too. You want to return to make sure no code after the line gets executed and send another response again accidentally.
E.g.: return res.json("bio Done!")

Node.js mongodb trouble with callbacks

So I'm trying to create a sign up route that checks to see if the user exists first and i have the database call in a separate function that needs to return true or false when it's done. The problem is i'm not very familiar with callbacks and the whole asynchronous thing everything that i have searched for does not seem to work keeps giving me.
TypeError: callback is not a function
This is my code any help or direction would be appreciated.
function pullUserFromDatabase(username, callback) {
console.log(username); //for debug
mongodb.connect(url, function(err, db) {
if(err) {
console.log("didn't get far" + err) //for debug
}
var collection = db.collection(username);
collection.findOne({username}, function(err, item) {
if(err) {
console.log("nope it broke" + err) //for debug
} else {
console.log("it worked" + JSON.stringify(item)) //for debug
callback(true);
}
});
});
}
app.post("/signup", function(req, res) {
var username = req.headers["username"],
password = req.headers["password"],
randomSalt = crypto.randomBytes(32).toString("hex"),
passwordHashOutput = crypto.createHash('sha256').update(password + randomSalt).digest("hex");
if(!username || !password) {
res.send("Username or password not provided.")
} else if(pullUserFromDatabase(username)) {
res.send("User exist.")
}
});
You need to use the callback as follows:
function pullUserFromDatabase(data, callback) {
console.log(data.username); //for debug
mongodb.connect(url, function(err, db) {
if(err) {
console.log("didn't get far" + err) //for debug
}
var collection = db.collection(data.collection);
collection.find({"username": data.username}).count(function (err, count) {
callback(err, !! count);
});
});
};
app.post("/signup", function(req, res) {
var username = req.headers["username"],
password = req.headers["password"],
randomSalt = crypto.randomBytes(32).toString("hex"),
passwordHashOutput = crypto.createHash('sha256').update(password + randomSalt).digest("hex");
if(!username || !password) {
res.send("Username or password not provided.")
}
var data = {
username: username,
collection: "collectionName"
}
if(!username || !password) {
res.send("Username or password not provided.")
}
pullUserFromDatabase(data, function(err, exists) {
if (err) {
res.send(400, "Error - " + err);
}
else if(exists) {
res.send(200, "User exists.");
}
res.send(200, "User does not exist.");
});
});
The reason that callback is undefined is because you didn't pass a 2nd argument to pullUserFromDatabase(username) Provide a 2nd argument, eg. pullUserFromDatabase(username, function(result) {/* do something here with the result variable */})
If you're not very familiar with aync & callbacks, you might find it more intuitive to use promises, but that comes with its own learning curve.
In the context of the original code, this looks like:
...
if(!username || !password) {
res.send("Username or password not provided.");
return;
}
pullUserFromDatabase(username, function(result) {
if(result) {
res.send("User exist.");
} else {
// TODO: Handle this case. If res.send() is never called, the HTTP request won't complete
}
});
...
Also, you need to ensure your callback is always invoked. Add callback(false):
console.log("nope it broke" + err); //for debug
callback(false);
Do a similar step after "didn't get far" and then return so the callback doesn't get invoked multiple times.

Categories

Resources