function is running before sql query is finished, node js - javascript

i have made an event listener for my server which listens for when someone is trying to log on, so i do an sql query and then if the info matches, it logs them on. problem is, its testing if the info matches before it runs the query so it will always return false. here is my code
player.on('checkLogin', function(data)
{
var logIn = "";
connection.query({
sql: "SELECT * FROM users WHERE username = ?",
values: [data.user] },
function(error, results, fields)
{
if (error)
{
console.log(error);
}
if(results)
{
var pass = "";
for (i = 0; i < results.length; i++)
{
pass = (results[i].password);
}
var input = crypto.createHash('md5').update(data.pass).digest("hex");
if (pass == input)
{
logIn = true;
}
}
});
if (logIn == true)
{
this.em.emit('login',
{
id: player.playerid
});
}
}.bind(this));
I heard promises will fix this but is there any easy way around this?, thanks in advance

The issue you have is your if(login == true) is outside of the completion handlers of the query.
Move the if(logIn == true) into your completion handler.
player.on('checkLogin', function(data)
{
var logIn = "";
connection.query({
sql: "SELECT * FROM users WHERE username = ?",
values: [data.user] },
function(error, results, fields)
{
if (error)
{
console.log(error);
}
if(results)
{
var pass = "";
for (i = 0; i < results.length; i++)
{
pass = (results[i].password);
}
var input = crypto.createHash('md5').update(data.pass).digest("hex");
if (pass == input)
{
logIn = true; //<-- could just handle it here.
}
if (logIn == true) //<-- moved inside completion handler
{
//log on
}
}
});
}.bind(this));
Now the reason it is being called is your connection.query runs asynchronously which is why the function handler is used. Code outside the completion handler will be run immediately.
EDIT from Additional Changes
Based on your changes your this scope will be changed when moved inside the completion handler function. To get a reference back you will need a reference to the player scope (assuming that is what this is). So that can be handled simply by creating a variable of this as var that = this; at the top of your checklogin function. Something like:
player.on('checkLogin', function(data)
{
var that = this; //create a variable to store the scope (this)
var logIn = "";
connection.query({
sql: "SELECT * FROM users WHERE username = ?",
values: [data.user] },
function(error, results, fields)
{
if (error)
{
console.log(error);
}
if(results)
{
var pass = "";
for (i = 0; i < results.length; i++)
{
pass = (results[i].password);
}
var input = crypto.createHash('md5').update(data.pass).digest("hex");
if (pass == input)
{
logIn = true; //<-- could just handle it here.
}
if (logIn == true) //<-- moved inside completion handler
{
//log on
that.em.emit('login', //<-- reference `that` not `this`
{
id: player.playerid
});
}
}
});
}.bind(this));

Related

Async write after select Pouchdb

I'm trying to check if an element exist before inserting it in my bdd.
I have to do this in order to (in the future) modify this existing element.
I'm using PouchDb and PouchDb-find with Node 6.9.1.
Actually I'm doing this:
for(var i = 0; i < 10;i++ ){
(function(_count, _pdb){
var count = _count;
var db = _pdb;
db.find({
selector: {numeroCandidat: parseInt(results[count].no_apb)}
}).then((result) => {
if(result.docs.length != 0){
console.log("l'étudiant existe");
}else{
console.log("l'étudiant n'existe pas");
var etudiant = {
"numeroCandidat": results[count].no_apb,
"nom": results[count].nom,
"numeroGroupe": "gr" + results[count].groupe,
"filiere": results[count].libelle,
};
db.post(etudiant).then((response) =>{
// handle response
console.log("STUDENT CREATED");
}).catch(function (err) {
console.log(err);
});
}
}).catch(function (err) {
});
})(i, this.pdb);
};
But the problem is : Due to the asynchronous version of my select query... if an element exists two times it appends that the second select occurred BEFORE the insertion of the first element, and I have this element two times in my database. I don't know how to deal with this one.
SO.. I'v found a workaround !
Simply create a function that I call recursivly after writting into my database.
Goodbye for loop.
var createStudents = function(_count, _pdb, _students){
if(_count >= 10) return;
console.log(_count);
var count = _count;
var db = _pdb;
var students = _students.slice(0);
db.find({
selector: {numeroCandidat: parseInt(students[count].no_apb)}
}).then((result) => {
if(result.docs.length != 0){
console.log("l'étudiant existe");
createStudents(++count,db,results);
}else{
var etudiant = {
"numeroCandidat": students[count].no_apb,
"nom": students[count].nom,
"numeroGroupe": "gr" + students[count].groupe,
"filiere": students[count].libelle,
"etudiantComms": [
{"commentaire": students[count].commentaire}
]
};
db.post(etudiant).then((response) =>{
// handle response
console.log("STUDENT CREATED");
createStudents(++count,db,results);
}).catch(function (err) {
console.log(err);
});
}
}).catch(function (err) {
});
}
createStudents(0,this.pdb,results);

Integrating asynchronous mongo call within an inner forEach loop

I got two loops, the outer loops over the users and the inner one loops over the venueID's of each user. Within the inner loop I want to look up the venue and attach it to an array defined in the outer look (userItem). However because forEach is synchronous and the mongo database look up is asynchronous the result always remains empty. I've tried to integrate this answer but to no avail. How to do this?
ret = [];
users.forEach(function(user) {
var userItem = user.getSanitised('ADM');
userItem.venues = [];
var tmp = [];
userItem.adminVenueIds.forEach(function(adminVenueId){
tmp.push(function(callback) {
Venue.findOne({_id:adminVenueId}, function(error, venue) {
callback(null, venue.toObject());
});
});
});
async.parallel(userItem.venues, function(err, result) {
/* this code will run after all calls finished the job or
when any of the calls passes an error */
if (err)
return console.log(err);
userItem.venues.push(result);
});
ret.push(userItem);
});
Tried the following as well but doesn't work also
users.forEach(function(user) {
var userItem = [];
async.series({
setUserItem : function(callback)
{
userItem = user.getSanitised('ADM');
callback(null, 'OK');
},
setUserVenues : function(callback)
{
userItem.venues = [];
user.adminVenueIds.forEach(function(adminVenueId,index) {
Venue.findOne({_id:adminVenueId}, function(error, venue) {
userItem.venues.push(venue.toObject());
if((index+1) == user.adminVenueIds.length)
callback(null, 'OK');
});
});
}
},
function(error, results) {
if(error)
winston.error(error);
ret.push(userItem);
}
);
});
You could simply put an if statement (in your case put the conditional as the array length) then when the loop is done you could then make it continue doing its thing by calling a function to continue (or put your code in there, but it will start to look messy)
var ret = [];
var test = [];
for (var i = 0; i < 20; i++) {
for (var x = 0; x < 20; x++) {
setTimeout(function() {
test.push("Test"+x);
if (x === 20) {
finishIt();
}
}, 300)
}
}
function finishIt() {
console.log(test);
ret.push(test);
}
I think you might want to look into using Mongoose. It is a NodeJS application layer on top of MongoDB that provides a more SQL like experience.
http://mongoosejs.com
I ended up with the following solution. It's dirty but I guess that's just nodejs being nodejs.
users.forEach(function(user) {
var userItem = user.getSanitised('ADM');
userItem.venues = [];
user.adminVenueIds.forEach(function(adminVenueId) {
Venue.findOne({_id:adminVenueId}, function(error, venue) {
userItem.venues.push(venue.toObject());
});
});
(function(){
if(userItem.venues.length == user.adminVenueIds.length) {
ret.push(userItem);
} else {
setTimeout(arguments.callee, 30);
}
})();
});

node.js return data from find collection in loop

I am trying to return data from this function. Console.log(documents) successfully shows the data in console. But this works only in body of the function. I can't return this data to the template. What should I do? Should I use some async package for node.js, or can be accomplished somehow like this?
Thank you.
var projects = req.user.projects;
var docs = [];
db.collection('documents', function(err, collection) {
for (i = 0; i < projects.length; i++) {
collection.find({'_projectDn': projects[i].dn},function(err, cursor) {
cursor.each(function(err, documents) {
if(documents != null){
console.log(documents);
//or docs += documents;
}
});
});
}
});
console.log(documents); // undefined
res.render('projects.handlebars', {
user : req.user,
documents: docs
});
Those db functions are async, which means that when you try to log it, the function hasn't finished yet. You can log it using a callback, for example:
function getDocuments(callback) {
db.collection('documents', function(err, collection) {
for (i = 0; i < projects.length; i++) {
collection.find({
'_projectDn': projects[i].dn
}, function(err, cursor) {
cursor.each(function(err, documents) {
if (documents !== null) {
console.log(documents);
callback(documents);// run the function given in the callback argument
}
});
});
}
});
}
//use the function passing another function as argument
getDocuments(function(documents) {
console.log('Documents: ' + documents);
});

How To handle result set of websql in html 5?

I m creating mobile web application using html5 and javascript.I m having two javascript files. AttributesDatabase.js and AttributeView.js.From AttributeView.js i m calling one function from AttributeDatabase.js in that i m executing one select query.Now the query result should go to AtttributeView.js.But the Websql transaction is asynchronous call that is what it is not returning proper result.Is there any way to handle the websql result.
Please help if any way there?
Edited
AttributeView.js
var AttributeDAOObj = new AttributeDAO();
AttributeDAOObj.GetAttributeList();
alert(AttributeDAOObj.GetAttributeList()); //This alert is coming as undefined.
AttributeDAO.js
this.GetAttributeList = function () {
var baseDAOObj = new BaseDAO();
var query = "SELECT AttributeName FROM LOGS";
// this.Successcalbackfromsrc = this.myInstance.Successcalback;
var parm = { 'query': query, 'Successcalback': this.myInstance.Successcalback };
baseDAOObj.executeSql(parm);
}
//To Create database and execute sql queries.
function BaseDAO() {
this.myInstance = this;
//Creating database
this.GetMobileWebDB = function () {
if (dbName == null) {
var dbName = 'ABC';
}
var objMobileWebDB = window.openDatabase(dbName, "1.0", dbName, 5 * 1024 * 1024);
return objMobileWebDB;
}
//Executing queries and getting result
this.executeSql = function (query) {
var objMobileWebDB = this.myInstance.GetMobileWebDB();
objMobileWebDB.transaction(function (transaction) {
//In this transaction i m returning the result.The result value is coming.
transaction.executeSql(query, [], function (transaction, result) { return result; }, this.Errorclback);
});
}
}
The problem is in you succes call back (like in the comment to your question, stated by DCoder)
function (transaction, result) { return result; }
this is returning where to?
So this is how to do it (or at least one way)
you can do for example:
function (transaction,result){
console.log("yes, I have some result, but this doesn't say anything, empty result gives also a result");
// so check if there is a result:
if (result != null && result.rows != null) {
if (result.rows.length == 0) {
// do something if there is no result
}else{
for ( var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
var id = result.rows.item(i).id; //supposing there is an id in your result
console.log('Yeah! row id = '+id);
}
}
}else{
// do something if there is no result
}
};
note the code above can be compacter, but this is how to understand it better.
another way is to put this function is a seperate piece of code, so you keep the sql statement more compact and readable. Like you call you error callback this can be in your function (with this. in front of it) or a completely seperate function.

asynchronous call to return a value?

I am bulding a table that either shows a play button or a stop button. (and some other stuff)
for(var i = 0; i < result.length; i++){
var current = result[i].split("$");
if (CheckRunning(current[0])){
t = t + "<tr><td><img alt='stop' id='img"+i+"' src='stop.png' onclick='ChangeButton(\"img"+i+"\");'/>";
} else {
t = t + "<tr><td><img alt='play' id='img"+i+"' src='play.png' onclick='ChangeButton(\"img"+i+"\");'/>";
}}
The problem here is the CheckRunning method. It opens a database which is an asynchron method. I cant simply do a return true/false. So whats the solution? anyway, here is the code for it:
var tabel;
var running = false;
function CheckRunning(tabel){
this.tabel = "tabel"+tabel+"";
var db = window.openDatabase(this.tabel, "1.0", this.tabel, 1000000);
db.transaction(checkrunningDB, checkerrorCB);
console.log(this.running);
return this.running;
}
function checkrunningDB(tx) {
tx.executeSql('SELECT max(id), sluttime FROM '+this.tabel, [], checkrunningSuccess, checkerrorCB);
}
function checkrunningSuccess(tx, results) {
if (results.rows.item(0).sluttime != null){
this.running = false;
} else{
this.running = true;
}
}
function checkerrorCB(err) {
this.running = false;
console.log(err);
}
Pass a callback function to CheckRunning:
CheckRunning(current[0], function(isRunning){...})
...
function CheckRunning(tabel, callback)
{
var isRunning = null; // we don't know yet
....
callback(isRunning);
}
It's similar to your tx.executeSql function which takes checkRunningSuccess as a callback. In that case the function name is hard coded as checkRunningSuccess, you could do the same in checkRunning.
By the way, if this is a public webapp running SQL queries makes you vulnerable to SQL injection attacks.

Categories