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

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

Related

My callback function not working

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);
}

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
});

Using Callbacks

I'm trying to use a callback to get the result of a query from a database, but I am still getting undefined returned to me. Here is the code:
function onComplete(x){
return x;
}
function getRecords(callBack){
mongo.MongoClient.connect("mongodb://localhost:27017/nettuts", function(err, db){
if(err){
console.log(err);
return;
}
console.log('Connected to mongo');
db.collection('nettuts').find().toArray(function(err, records){
if(typeof callBack === "function"){
callBack(records);
}
});
});
}
app.get('/', function (req, res) {
var records = getRecords(onComplete);
console.log(records);
res.render("index", {title: "Hello!", people: records});
});
On the third to last line, I am getting an undefined.
As stated, you are following this the wrong way around for asynchronous programming, if you want "re-use" then you are passing in the callback to the function you are re-using and not the other way around:
var mongo = require("mongodb"),
MongoClient = mongo.MongoClient;
function getRecords(db,callback) {
if (typeof callback !== 'function')
throw "getRecords() requires a callback as the second arg";
db.collection("test").find().toArray(function(err,records) {
callback(err,records);
});
}
MongoClient.connect('mongodb://localhost/test',function(err,db) {
getRecords(db,function(err,result) {
if (err) throw err;
console.log(result);
});
});
Also noting here that any of your active code needs to happen "after" the connection is present, too broad to cover here on the best way to do that. But you certainly do not establish a connection with every request, as that would be very bad.
You "re-usable" function then just accepts the connection details and the actual functionality you want to happen when the operation is complete. That is the basic principle of callbacks.

How to return results of node's sqlite3 in a function?

I'm trying to use sqlite3 in an expressjs app (nodejs)
I want to create a function that returns all the results from a select statement. This function will be called by a route that
var queryGetAll = 'SELECT id, title, description, modified, lat, lng, zoom FROM maps';
function Manager(){
this.db = null;
this.getAll = function(){
var all = [];
this.db.all(queryGetAll, function(err, rows){
if (err){
throw err;
}
all.push.apply(all, rows);
});
return all;
}
}
I know nodejs is asynch, so it means the return is called before the end of the query. But I don't find examples on how I should use sqlite.
The line "return all" in your example will be executed BEFORE this.db.all() calls your callback. In order for your code to work you need to do something like this:
var queryGetAll = 'SELECT id, title, description, modified, lat, lng, zoom FROM maps';
function Manager(){
this.db = null;
// Allow a callback function to be passed to getAll
this.getAll = function(callback){
this.db.all(queryGetAll, function(err, rows){
if (err){
// call your callback with the error
callback(err);
return;
}
// call your callback with the data
callback(null, rows);
return;
});
}
}

Categories