My callback function not working - javascript

I am do a nodeJS application, in which there is a call back function to persist the value to the database(MongoDB). The callback function as below:
exports.add = function(student, cb) {
var collection = db.get().collection('students');
collection.insert(student, function(err) {
if (err) {
throw err;
}
console.log("Record added");
});
}
The above call back function is called from the another fuction as below:
router.post("/add", function(req,res){
var student = req.body;
Students.add(student, function(err) {
if (err) {
throw err;
}
var respOut = JSON.stringify({id:student.id});
console.log("respOut");
res.send(respOut);
});
});
The call back function is called from the above bolded(Students.add) section. Currently I am able to persist the data on to the DB. But not getting any output on the console( given console out as -> console.log("respOut"); or any response in the UI(from above code). Is there any problem with my call back function? or I am missing anything?

You're not using cb in your Students.add method.
The easiest would be to just pass the callback through to collection.insert:
exports.add = function(student, cb) {
var collection = db.get().collection('students');
collection.insert(student, cb);
}

Related

NodeJs - Making 'GET' request conditionally in a while loop with request module

I am accessing an API endpoint that is a little iffy. About 70% of the time it sends back the correct JSON response, but a few times it will crap out and send an XML that says "values /values". I want to make a while loop what requests until it gets the correct response back. In my case, I am guaranteed to get back the correct response eventually, so that is why I am looping instead of figuring out why the endpoint is crapping out.
Here is my code:
var gotValidResponse = false;
while(!gotValidResponse){
request(options, function(err, res, body){
if(err){
console.log(err);
return;
}
try{
data = JSON.parse(body);
console.log(data);
gotValidResponse = true;
}catch(e){
console.log('trying again');
}
});
}
processJSON(data);
Obviously the code above does not work, but hopefully it is showing what I am trying to do. Thanks for any help!
EDIT: Like this?
var myData = getStuff(options);
function getStuff(options){
request(options, function (err, res, body) {
if (err) {
console.log(err);
return
}
try {
data = JSON.parse(body);
return data;
} catch (e) {
return getStuff(options);
}
})
}
You almost got it right in your edit. What you need to do is keep calling the function until it returns what you want. Like this (my conditionals are merely illustrative):
var attemps = 1;
var fake = function(data, cb) {
console.log('Attemp nº', attemps);
if(attemps < 5) {
attemps += 1;
return fake(data, cb);
} else {
return cb(null, 'completed');
}
}
fake('whatever', function(err, res) {
console.log(res)
})
https://jsfiddle.net/eysu2amp/
If you check the console, you will see that the fake function gets called 5 times and then returns the data. The recursive calling of the function keeps passing the same callback function.

Cant call mongodb methods from a function

So i made a user login, the database is mongodb and it works perfect. However, i want to to call the mongodb again after the person logins in, so i decided to split it up into separate functions. However, when i tried taking the login script and turn in into a function, the database wont open. Which makes no sense, since im using the exact same code that worked inside the route. Can this be done in a function? Does anyone know what’s going on?
Below is the code and images of the debugger proving the dB wont open.
Route
// Listen for Upload file
router.post('/uploadFile', function (req, res) {
upload(req, res, function (err) {
if (err) {
console.log("Error uploading file");
} else {
//var databaseName = "E-learn", collection = "Accounts";
var username = req.body.username ;
var fileName = req.file.filename ;
var filePath = req.file.path ;
console.log(username);
console.log("GET " + req.file.fieldname);
console.log("GET " + req.file.filename);
console.log("GET " + req.file.orignalName);
console.log("GET " + req.file.path);
var result = findOne(username);
res.json(result);
}
});
});
Function call
function findOne(username){
var databaseName = "E-learn", collection = "Accounts";
var db = new Db(databaseName, new Server('localhost', 27017));
db.open(function (err, db) {
console.log(databaseName + ": opened");
console.log(db);
db.collection(collection).findOne({username:username}, function (err, doc) {
assert.equal(null, err);
if (doc != null) {
console.log("Found");
// db.close();
return "Found" ;
} else {
console.log("Not Found");
// db.close();
return "Not found";
}
db.close();
});
});
console.log("Did not open")
db.close();
return 0 ; // Should not be called
}
Not sure what ES version you're running but if you want to take the synchronous approach, try this version of findOne. Async/Await makes asynch code synchronous.
async function findOne(username){
var databaseName = "E-learn", collection = "Accounts";
var db = new Db(databaseName, new Server('localhost', 27017));
let db = await db.open();
// try above line first
//let {err, db} = await db.open();
let doc = await db.collection(collection).findOne({username:username});
// try above line first
//let {err, doc} = await db.collection(collection).findOne({username:username});
//assert.equal(null, err);
if (doc != null) {
console.log("Found");
// db.close();
return "Found" ;
} else {
console.log("Not Found");
// db.close();
return "Not found";
}
db.close();
//console.log("Did not open")
//db.close();
//return 0 ; // Should not be called
}
If you get an error for Async/Await, try installing this package. https://www.npmjs.com/package/asyncawait
There are couple of issues in your code as observed on first look.
Use of return in asynchronous functions to return a result will not work.
You need to define a callback function which you pass as reference to findOne. This callback function needs to be called to return the result.
Printing of "Did not open" in this scenario does not mean Database did not open. This is asynchronous execution so database may open after printing of that console log.
You are facing the typical asynchronous callback problem that most newly introduced JavaScript developers experience.
First of all, JavaScript is asynchronous (Must read for you). That means that when you pass a function as a parameter of something, you can't expect that the code inside the function will run inline to the code. Maybe the function that you pass will be called as a result of an event (connect to something, user clicked a button, a database is opened...), so will happen sometime in the future. Even if is 1 nanosecond after, will be in the future.
So you expect it to run this way:
function findOne(username){
// Start with this (step 1)
var databaseName = "E-learn", collection = "Accounts";
// Then continue with this (step 2)
var db = new Db(databaseName, new Server('localhost', 27017));
// Open the database (step 3) and the code will wait for the database to open
db.open(function (err, db) {
// Run this inline, just after db.open (step 4)
console.log("OPEN");
[...]
});
// Continue with this after the database was open (step 5)
console.log("Did not open")
db.close();
return 0 ; // Should not be called
}
But actually what is happening is this:
function findOne(username){
// Start with this (step 1)
var databaseName = "E-learn", collection = "Accounts";
// Then continue with this (step 2)
var db = new Db(databaseName, new Server('localhost', 27017));
// Open the database (step 3), pass a function that will be called by the database when is open AND continue with the next step
db.open(function (err, db) {
// This function will be called when the database is open, so right now is not called.
console.log("OPEN");
[...]
});
// This is the next step after step 3 (step 4).
console.log("Did not open")
db.close();
return 0 ; // Should not be called
}
// And sometime in the future you suddenly get in console OPEN, when the database decides to run the callback you passed to it.
Another way to look into this is that a return will always return from a function, so having this nested function:
function findOne(username) { // function keyword, so this is a function (number 1)
[...]
db.open(function (err, db) { // Note the function keyword here, so this is a function too (number 2)
[...]
return 1; // This return actually works, BUT affects the function where it belongs, which is function number 2 in this case.
});
[...]
return 0; // And this one is the return of the function number 1.
}
So imagine that db.open runs the callback immediately, before running the very next line of code. Although it is not ran in an asynchronous way, the return inside the callback still can't return the findOne function.
Async problems require async solutions. (Or use ES6 generators (async, await, etc...), but currently will complicate you more as you won't still understand what is happening with your code, and worse, when to use generators (as they are tied to async callbacks), so better first understand async callbacks in JS). When you understand it, is pretty easy actually. Just a few changes:
// Listen for Upload file
router.post('/uploadFile', function (req, res) {
upload(req, res, function (err) {
if (err) {
console.log("Error uploading file");
} else {
//var databaseName = "E-learn", collection = "Accounts";
var username = req.body.username ;
var fileName = req.file.filename ;
var filePath = req.file.path ;
console.log(username);
console.log("GET " + req.file.fieldname);
console.log("GET " + req.file.filename);
console.log("GET " + req.file.orignalName);
console.log("GET " + req.file.path);
// Converted the sync function to an async one, by passing a
// callback function as a parameter with 2 arguments, being
// the first the possible error and the second the data
findOne(username, function(err, result) {
if (err) {
console.log("Error uploading file");
} else {
res.json(result);
}
});
}
});
});
// Here you can see now that we have the callback parameter,
// that references the callback function we passed before and
// we can call it whenever we want
function findOne(username, callback) {
var databaseName = "E-learn", collection = "Accounts";
var db = new Db(databaseName, new Server('localhost', 27017));
db.open(function (err, db) {
if (err) {
callback(err); // If error, pass the error as first argument of the callback
} else {
console.log(databaseName + ": opened");
console.log(db);
db.collection(collection).findOne({username:username}, function (err, doc) {
if (err) {
callback(err); // As findOne is another async callback too, the same as above. Check for err
} else {
// And if everything is fine, then pass the result as the second parameter of the callback
if (doc != null) {
callback(null, "Found");
} else {
callback(null, "Not found");
}
}
db.close();
});
}
});
}
As you may have noticed, you pass a lot of function as callbacks, in the database methods and inside the router methods too.
And some developer protips:
Now simply read the code line by line and try to understand what is happening on each one. This way you may wondered "Why the code I've not written is made by callbacks and mine not?". This question and a bit of research will helped you a lot.
Always have clean indentation. Your code have some problems with it. Indentation is a must in JavaScript where you can have a callback hell because of his asynchronous nature, and indentation helps a lot with it.

How to force javascript not to make callback functions parallel?

I'm building a twitter clone using Node.js and MongoDB with mongoose. My Tweet model has body, user and created fields where user is the id of the user who has created the tweet. Now I'm building the API. I want when I make a GET request to receive a list of all the tweets (/api/tweets/) but except the user field (which returns only the id of the user) I want to get the whole user object so that I can display information about the tweet owner in my front-end part. I ended up with the following code.
exports.all = function (req, res, next) {
Tweet.find({}, function (err, tweets) {
if (err) return res.json(400, err);
var response = [];
tweets.forEach(function (element, index, array) {
var tweet = {};
tweet._id = element._id;
tweet.created = element.created;
tweet.body = element.body;
User.findById(element.user, function (err, user) { // <- This line
if (err) return res.json(400, err);
tweet.user = user;
});
response.push(tweet);
});
return res.json(response);
});
};
It works perfectly except that it doesn't add the user info. The problem is in the line I have marked. When javascript comes to that line, it tries to make it "parallel" and continues with the code without executing the callback function. But then it pushes the tweet object that doesn't have yet user info. How can I fix this?
You're going to want to use the async library. It will make your life much easier.
// inside `Tweet.find`
async.each(tweets, function(done) {
// do stuff to tweets
User.findById(element.user, function(err, user){
if (err) return done(err);
// do stuff with user
done();
});
}, function(err) {
// called when done
res.json(response);
});
The issue is that res.json sends the response so it doesn't matter that findById is being called. You need to call res.json once everything is done.
You can do this in several ways, but the easiest one with your existing code is to just keep a counter:
var tweetCount = 0;
tweets.forEach(/* snip */) {
User.findById(element.user, function (err, user) {
tweet.user = user;
tweetCount++;
response.push(tweet);
if (tweetCount == tweets.length) {
res.json(response);
}
});
});
You can use Q promise library to sync. It is simple and easy to use.
Response will only be send when whole promise chain is compeleted
var result = Q();
tweets.forEach(function (tweet, i) {
result = result.then(function () {
// do your stuff
}
result.then(function () {
res.json(response); //successfully completed
});

How to pass a variable to a predefined callback function in node.js

Using node.js, socket.io is calling onRegister() to check the mongoDB for a user.
But the DB callback function is predefined, how can I add 'this' (ioHandle) to the callback parameters?
function onRegister(data) {
var name,pass,room;
name = data.name;
pass = data.pass;
ioHandle = this;
Mongo.connect('mongodb://127.0.0.1:27017/main', function(err, db, ioHandle) { // wrong
if(err) throw err;
var collection = db.collection('users');
// does user exist
collection.findOne({name : name}, function(err, doc, ioHandle) { // wrong
if(err) throw err;
if(doc) {
log("User already exists");
ioHandle.emit(NGC_REGISTER_RESULT, {NGC_REJECT:"User already Exists"}); // ioHandle undefined
} else {
// create new user
log("User not found");
ioHandle.emit(NGC_REGISTER_RESULT, NGC_ACCEPT); // ioHandle undefined
}
db.close();
});
});
}
The error: ioHandle isn't being passed
TypeError: Cannot call method 'emit' of undefined
You don't need to add ioHandle to the findOne callback function, ioHandle will be in scope for that function through normal JavaScript closure mechanics:
function onRegister(data) {
// ioHandle will be visible to everything inside this function,
// that includes the callback and nested callback below.
var ioHandle = this;
//...
Mongo.connect('mongodb://127.0.0.1:27017/main', function(err, db) {
//...
collection.findOne({name : name}, function(err, doc) {
// Use ioHandle as normal in here
//...
You might want to spend a bit of time on the MDN closures page.

how to make Nodejs call synchronous then I can use it easily?

app.post('/login', function (req, res) {
var login = req.body.login;
var pass = req.body.pass;
var ret = CheckUserValid(login, pass);
res.send(ret);
})
function CheckUserValid(login, pass,callback) {
var sql = "SELECT * FROM `user` WHERE login = ? AND pass= ?";
client.query(sql, [login, pass], function selectResutl(err, results, fields) {
console.log(results);
if (!err) return true;
else
throw err;
});
}
first Function is about request and second is about making call to mysql. because it's async so it's not worked.
Can someone let me know how I can make it work synchronously like in C#.
The signature of your CheckUserValid method already implies a callback.
You can use that to call something when your db-request is done, like this:
app.post('/login', function (req, res) {
var login = req.body.login;
var pass = req.body.pass;
// use a closure here for the callback of checkUserValid
checkUserValid(login, pass, function(err, isValid) {
// send the response when checkUserValid is done
res.send(isValid)
});
})
function checkUserValid(login, pass, callback) {
var sql = "SELECT * FROM `user` WHERE login = ? AND pass= ?";
client.query(sql, [login, pass], function(err, results, fields) {
console.log(results);
if (!err) {
// there is no error, pass null for the error
// and true for the result
callback(null, true);
} else {
// an error occurred in the db-query
// pass it to the callback
callback(err)
}
});
}
By convention you should pass the err as first argument, but in the end that's up to you.
However you can pass the original error up to your initial function and handle it there.
You should a look at Fibers module.
You can chain the functions that you want to call in order
e.g. you can make res.send(ret); as the callback of your CheckUserValid

Categories