I have a script in nodeJS that connects to my postgres DB, the issue is that on a callback method I'm initiating a variable but it seems always empty, this my app.js script :
var ck_username = /^[A-Za-z0-9_]{1,20}$/;
function fncCheckUsernameAvailability(vstrUsername, callback) {
var pg = require("pg");
var client = new pg.Client({user: 'xxx', password: 'xxx', database: 'xxx', host: 'xxx.com'});
client.connect(function(err) {
if (err) {
callback(err, null);
return;
}
client.query("SELECT username FROM users WHERE username ='"+vstrUsername+"'", function(err, result) {
if (err) {
callback(err, null);
return;
}
if (result.rows[0] == undefined) {
callback(null, null);
}else {
callback(null, 'Username already taken');
}
client.end();
});
});
}
app.post("/Signup", function(req, res){
var username = req.body.username;
var usernameError = '';
var errors = [];
if (!ck_username.test(username)) {
errors[errors.length] = "Invalid username";
usernameError = "Invalid username";
}
if (ck_username.test(username)) {
//The issue starts here
fncCheckUsernameAvailability(username, function(err, result) {
if(result != null){
errors[errors.length] = result;
usernameError = result; // usernameError is always empty
console.log(result);//This is printed in console, I can see the result
}
});
}
if (errors.length > 0) {
res.render('Signup.ejs', {
layout:false,
usernameError:usernameError // usernameError is sent to the view empty
});
}
else{
res.render('Signup.ejs', {
layout:false,
usernameError:'No errors'
});
}
});
The result is displayed on cansole so Can someone tell me why this variable is not instantiated , is this callback an asynchronous one?
is this callback an asynchronous one?
Yes.
//This line executes at time 1
if (ck_username.test(username)) {
//The issue starts here
fncCheckUsernameAvailability(username, function(err, result) {
//this section of code executes at time 3! Oh snap async blew my mind!
if(result != null){
errors[errors.length] = result;
usernameError = result; // usernameError is always empty
console.log(result);//This is printed in console, I can see the result
}
});
}
//This section of code executes at time 2 which is BEFORE time 3
if (errors.length > 0) {
res.render('Signup.ejs', {
layout:false,
usernameError:usernameError // usernameError is sent to the view empty
});
}
You have to move ALL the code that requires the result variable INSIDE that callback function. Putting it outside the function but lower in the file makes things execute in the wrong order.
Also checkout http://callbackhell.com for guidance on adjusting to code organization in an async environment.
Yes, the callback is asynchronous, which can execute at any time. It is probably then not yet defined at the time you are accessing. Therefore, you should access the variable from inside the callback, so it will always be defined at that point.
app.post('/Signup', function (req, res) {
var username = req.body.username;
if (!ck_username.test(username)) {
res.render('Signup.ejs', {
layout: false,
usernameError: 'Invalid username'
});
} else {
fncCheckUsernameAvailability(username, function (err, result) {
if (result != null) {
res.render('Signup.ejs', {
layout: false,
usernameError: result
});
} else {
res.render('Signup.ejs', {
layout: false,
usernameError: 'No errors'
});
}
});
}
});
Here is an example of how callbacks work, and why your variable didn't return the expected value. Take these two functions:
var foo = function(num, callback) {
setTimeout(function() {
callback(num * 2);
}, 5000);
};
This function multiplies the number by two, but after 5 seconds. If you try to access the variable before those 5 seconds, you don't have a value, like this case:
var num = 0;
foo(5, function(result) {
num = result;
});
console.log(num); // returns 0
In this case, num is only set to 10 after 5 seconds, console.log() will have executed before that time has passed.
Related
I am using google places api to get a list off cafes nearby. So,the api provides max 60 places but they are present in 3 different pages.To get the list of all 60 places, it provides tokens in the first and second page with the help of which we can access remaining places. I wrote a function that stores the place ids in an array.The places from first page is storing fine. But, in the first request i am accessing the token for the next page. The problem i am facing is, the request for second page is being called before assigning of the token.So, it results in an error. How can i make the reqeust for second page to wait until the token value is assigned?
const request= require('postman-request');
const nearbycafes = (latitude,longitude,callback) => {
var placeids=[];
let token;
let count=0;
const url='https://maps.googleapis.com/maps/api/place/nearbysearch/json?location='+latitude+','+longitude+'&radius=1500&type=cafe&key=xyz'
request({url: url, json: true},(error,response)=>{
console.log('ssss');
if(error){
callback('Error connecting to the API',undefined)
}
else if(response.body.results.length==0){
callback('No such address exist. Try something else',undefined)
}
else{
let i=0;
//for(i;i<response.body.results.length;i++){
placeids.push(response.body.results[0].place_id)
//}
if(response.body.next_page_token){
token=response.body.next_page_token;
count++;
console.log(count);
}
}
callback(undefined,{
placeids
})
})
console.log(count);
// if(count===1){
// const url2='https://maps.googleapis.com/maps/api/place/nearbysearch/json?location='+latitude+','+longitude+'&radius=1500&type=cafe&key=xyz='+token+''
// request({url: url2, json: true},(error,response)=>{
// console.log('ssss2');
// if(error){
// callback('Error connecting to the API',undefined)
// }
// else if(response.body.results.length==0){
// callback('No such address exist. Try something else',undefined)
// }
// else{
// let i=0;
// for(i;i<response.body.results.length;i++){
// placeids.push(response.body.results[i].place_id)
// }
// if(response.body.next_page_token){
// token=response.body.next_page_token;
// count++;
// }
// }
// callback(undefined,{
// placeids
// })
// })
// }
}
module.exports = nearbyhospitals;
You need to write a recursive function to get all the pages sequentially:
const request = require('postman-request')
function getPage (lat, long, token, cb) {
const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${long}&radius=1500&type=cafe&key=xyz${token ? `&token=${token}` : ''}`
request({ url: url, json: true }, (error, response) => {
if (error) {
cb(new Error('Error connecting to the API'), undefined)
} else if (response.body.results.length === 0) {
cb(null, [])
} else {
if (response.body.next_page_token) {
getPage(lat, long, response.body.next_page_token, (err, res) => {
if (err) {
return cb(err)
}
cb(undefined, res.concat(response.body.results))
})
} else {
cb(undefined, response.body.results)
}
}
})
}
function nearbycafes (latitude, longitude, callback) {
getPage(latitude, longitude, undefined, callback)
}
module.exports = nearbycafes
// just to try
nearbycafes(43.834527, 13.258358, (err, res) => {
console.log({ err, res })
})
Unfortunately, I cannot see the 'second' request in the code above. I assume the second request is being done in the 'callback' function. If you want to be sure that 'token' is not empty when 'callback' is executed, simply call the callback directly after assignment:
else{
let i=0;
//for(i;i<response.body.results.length;i++){
placeids.push(response.body.results[0].place_id)
//}
if(response.body.next_page_token){
token=response.body.next_page_token;
callback(undefined, { placeids: placeids });
count++;
console.log(count);
}
}
It appears that the positive (I assume) 'callback' call is outside of the 'else' clause. You sure you got the braces correct?
Best approach, though, is to avoid such 'global' variables as token in this case. You could parametrize callback and pass the token via function call. The rule of thumb in such chained requests is as follows:
function A(callback) {
$.ajax({
//...
success: function (result) {
//...
$.ajax({
// ...
success: function (result) {
// ...
}
})
}
});
}
I am creating a 'refresh data' function in Node and I cannot figure out where to place the callbacks and returns. The function continues to run. Below is a list of things the function should do. Could someone help out?
Check if a user has an api id in the local MongoDB
Call REST api with POST to receive token
Store token results in a MongoDB
Terminate function
./routes/index.js
router.post('/refresh', function(req, res) {
var refresh = require('../api/refresh');
refresh(req, function() { return console.log('Done'); });
});
../api/refresh.js
var callToken = require('./calltoken');
var User = require('../models/user'); // Mongoose Schema
module.exports = function(req, callback) {
User.findOne( {'username':req.body.username}, function(err, user) {
if(err) { console.log(err) }
if (user.api_id == 0) {
callToken.postToken(req.body.username, callback);
} else { // Do something else }
});
};
./calltoken.js
var request = require('request');
var Token = require('../models/token'); // Mongoose Schema
module.exports = {
postToken: function(user, callback) {
var send = {method:'POST', url:'address', formData:{name:user} };
request(send, function(err, res, body) {
if(err) { console.log(err) }
if (res.statusCode == 201) {
var newToken = new Token();
newToken.token = JSON.parse(body).access_token['token'];
newToken.save(function(err) {
if(err) { console.log(err) }
return callback();
});
}
});
}
};
I'm not an expert in Express but everywhere in you code in lines with if(err) { console.log(err) } you should stop execution (maybe of course not - up to you app) and return 400 or 500 to client. So it can be something like
if(err) {
console.log(err);
return callback(err); // NOTICE return here
}
On successful execution you should call return callback(null, result). Notice null as a first argument - it is according nodejs convention (error always goes as first argument).
I am mew to node js, I have something like this,
get_contacts(data, function(contacts) {
if (contacts.length) {
var count = contacts.length;
for (var i = 0; i < count; i++) {
result = {
id: contacts[i].id,
name: contacts[i].name,
sent1: get_sent(data.userId, contacts[i].id, function(resp) {
result.sent = resp.count;
}),
}
result1[i] = result;
}
output = {
contacts: result1,
}
} else {
output = {
error: "No Contacts.",
}
}
res.writeHead(200, {'content-type': 'text/html'});
res.end(JSON.stringify(output));
});
get_contacts is a callback function which will return contact list.result1 & result are objects. Now value for sent should come from a function get_sent, and get sent is like this
function get_sent(userId, contactId, callback) {
pool.getConnection(function(err, connection) {
connection.query("my query here", function(err, rows) {
connection.release();
if (!err) {
callback(rows);
} else {
console.log(err)
}
});
});
}
But im not getting any value since nodejs. since nodejs is async it is not waiting for the function to return value. I know, im doing it in wrong way. Please help
You need to use a callback. In simple words is a function that you'll execute after something happens. You should read more about that. You should get a book about javascript but you can start reading here for example.
About your case, you could solve it like this
//Asumming that you object `result` is global.
result = {
id: contacts[i].id,
name: contacts[i].name,
sent: -1 //Some default value
}
//Just to put the code into a function, you have to put it where you need
function constructObject (){
get_sent(uId, cId, function(err, total){
if(err){
console.log("Something was wrong.", err);
}
result.sent = total;
//Here you have your object completed
console.log(result);
});
}
//You need to use a callback
function get_sent(uId, cId, callback) {
pool.getConnection(function(err, connection) {
//Note that I add an alias here
connection.query("SELECT count(*) as total FROM table_name", function(err, rows) {
connection.release();
if (!err) {
//I am returning the result of the query and a null error
callback(err, rows[0].total);
} else {
console.log(err);
//I am returning an error
callback(err);
}
});
});
}
//For example you could call this function here
constructObject();
And it depends of what are you doing exactly but Maybe you need a callback on your constructObject too.
I am using MongooseJS to connect to MongoDB
I am creating a a new Team. Once I save the team, I want to add a reference to the team for my user. But unfortunately, whenever I go to add the team to my user and save the user my callback is not called. Any ideas why?
TeamRepository.prototype.createForUser = function(user, data, callback) {
var team,
_this = this;
if (user == null) {
user = null;
}
if (data == null) {
data = {};
}
if (callback == null) {
callback = (function() {});
}
team = new Team({
name: data.name,
description: data.description,
state: data.state
});
return this.save(team, function(err) {
if (err) {
return callback(err, team);
} else {
user.teams.push({
team: team._id
});
return _this.save(user, function(err) {
return callback(err, team);
});
}
});
};
Specifically this line. Notice I have two nested saves.
return _this.save(user, function(err) {
return callback(err, team);
});
Any help would be great.
I have solved the issue. In my user model, I had hooked into the save event and was not calling next() to move along the save.
schema.pre('save', function(next) {
var _this = this;
if (this.isNew || this.isModified('password')) {
return hashPassword(this.password, function(err, hash) {
if (err) {
return next(err);
}
_this.password = hash;
return next();
});
} else {
return next(); // missing this
}
});
And here is what my TeamRepository.save() looks like since you asked.
TeamRepository.prototype.save = function(model, callback) {
if (model == null) {
model = null;
}
if (callback == null) {
callback = (function() {});
}
if (model) {
model.increment();
return model.save(callback);
} else {
if (callback) {
return callback(null);
}
}
};
I'm trying to add a basic authentication to my Express paths. To check if the combination user/pass is correct, I read the collections usermodels in my MongoDB database.
Here is my code :
server.js
var normal = express.basicAuth(normalAuth);
app.get('path', normal, function(req, res){etc.});
function normalAuth(user, pass) {
var mongo = require('./database/userManager');
var result = false;
mongo.start(mongo.list(function(err, data) {
if(err) { throw err; }
else {
console.log('Getting list from db...');
for(var i in data) {
console.log('Getting details from db...');
if(user == data[i].name && pass == data[i].pass) {
console.log('New connection : ' + user);
result = true;
break;
}
else {
console.log('Failed connection : ' + user);
result = false;
}
}
console.log('OK');
}
mongo.mongoose.connection.close();
}));
return result;
}
userManager.js
var mongoose = require('mongoose');
var userModel = mongoose.connection.model('usermodel', new mongoose.Schema({
name: String,
pass: String,
status: { type: Boolean, default: false }
}), 'usermodels');
function start(callback) {
mongoose.connect('mongodb://localhost/derp', function(err) {
if(err) { throw err; }
else {
console.log('Connected to database ');
}
});
}
function list(callback) {
userModel.find(callback);
}
The authentication almost works, it connects to the db and get the informations. It prints on the console
"New connection : /*user connected*/".
But there is a problem : my function normalAuth always return false, so even if I have correct user/pass, I can't access the pages and I have the connection window as if I gave wrong user/pass. I guess it is because this function return the result before my mongo.start(blabla) finished, but I don't know what to do.
Thank you for your help.
You always get false result because in Node.js every IO operation is asynchronous.
Let me explain: When you call mongo.start(...) function, Node.js will put that in another operation queue because it is IO operation. Main operation queue must not be blocked, and that is why IO operations are always asynchronous. Ater mongo function is put in another queue of operations, your main operation queue will continue and get to the line of code: return false. And it will return false regardless what function mongo.start(...) will do with it.
Solution is by sending callback function:
function normalAuth(user, pass, callbackResult) {
var mongo = require('./database/userManager');
mongo.start(mongo.list(function(err, data) {
if(err) { throw err; }
else {
console.log('Getting list from db...');
for(var i in data) {
console.log('Getting details from db...');
if(user == data[i].name && pass == data[i].pass) {
console.log('New connection : ' + user);
// here you send true to callback function
callbackResult(true);
break;
}
else {
console.log('Failed connection : ' + user);
// here you send false to callback function
callbackResult(false);
}
}
console.log('OK');
}
mongo.mongoose.connection.close();
}));
}
And you call this function like this:
normalAuth(user, pass, function(result) {
if(result)
console.log("Auth OK");
else
console.log("Auth Failed");
}
EDIT: In express.basicAuth function it will be called like this:
app.use(express.basicAuth(function(user, pass, callback) {
normalAuth(user, pass, function(result) {
callback(null /* error */, result);
}
}));