Closures to get variable in node.js - javascript

I am working on thesis software for get variable in closures.
This is my code in node.js
var kepala = express.basicAuth(authentikasi);
// authenticate for login
function authentikasi(user, pass, callback) {
// declrare my database mongodb
db.collection('ak_teacher', function (err, data) {
data.findOne({
'tch_name': {
'$regex': user
}
}, function (err, level) {
console.log(level); // monitor data
if (level == null) {
console.log('Nilai database kepala sekolah masuk Null ulangi login');
callback(null);
} else {
var a = level.tch_name;
var b = level.tch_password;
var c = level.sch_id; // (i need this variable for next code)
var result = (user === a && pass === b);
console.log("id Sekolah : " + c);
callback(null /* error */ , result);
}
});
});
};
var tes = authentikasi(); // (in here i dont know declare for get my variable c)
app.get('/siswa_2', kepala, function (req, res) {
// i use variable in here
var sch_id = tes;
console.log("id school in query :" + sch_id);
console.log('Menampilkan Seluruh data Siswa');
db.collection('ak_student', function (err, collection) {
collection.find({
"sch_id": sch_id
}).toArray(function (err, items) {
console.log(items);
res.send(items);
});
});
});
I am trying to get variable c.

You need to pass c into the callback as well to get it's value:
callback(null /* error */, result, c);
Then call authentikasi like this:
// Note, since this callback is called by passing "c"
// as the third argument, this is how we assign it to "tes"
// |
// |
// V
authentikasi(user,pass,function(unused,result,tes) {
app.get('/siswa_2', kepala, function(req, res) {
var sch_id = tes;
console.log("id school in query :" +sch_id);
console.log('Menampilkan Seluruh data Siswa');
db.collection('ak_student', function(err, collection) {
collection.find({"sch_id":sch_id}).toArray(function(err, items) {
console.log(items);
res.send(items);
});
});
});
});

According to the docs, you're supposed to call back with a user object. You can simply put your c variable on there:
var kepala = express.basicAuth(authentikasi);
function authentikasi(user, pass, callback) {
// declrare my database mongodb
db.collection('ak_teacher', function (err, data) {
data.findOne({
'tch_name': {
'$regex': user
}
}, function (err, level) {
console.log(level); // monitor data
if (level == null) {
console.log('Nilai database kepala sekolah masuk Null ulangi login');
callback(null);
} else {
var a = level.tch_name;
var b = level.tch_password;
var c = level.sch_id; // (i need this variable for next code)
if (user === a && pass === b) {
console.log("id Sekolah : " + c);
callback(null, {name: a, id: c});
// ^^^^^^^^^^^^^^^^
} else
callback(null, false);
}
});
});
};
// don't try to authenticate outside of the request context
app.get('/siswa_2', kepala, function (req, res) {
var sch_id = req.user.id;
// ^^^^^^^^^^^
console.log("id school in query :" + sch_id);
…
});

You could also take advantage of locals:
Response local variables are scoped to the request, thus only
available to the view(s) rendered during that request / response
cycle, if any. Otherwise this API is identical to app.locals.
This object is useful for exposes request-level information such as
the request pathname, authenticated user, user settings etcetera.
Assigning c to res.locals.c should let you use it in later elements of that particular request and subsequent response.

Related

JS - How to return mysql query values properly

I'm pretty new to JS since I've switched from C#.
My Question is how I can return mysql query values without problems.
chat.registerCmd('getid', (player, arg) => {
db.getPlayerPermission(player.socialId, function(permission){
PermissionValue = permission;
});
var vname;
var name;
if(arg.length <= 1) {
chat.send(player, '/getid [Vorname] [Name]');
return;
}
vname = arg[0];
name = arg[1];
db.getCharIdByName(vname, name, function(charid){
chat.send(player, 'Die ID des Spielers ist: ' + charid);
});
});
Is this a good way to return query values?
export function getPlayerPermission(socialid, callback){
connection.query('SELECT permission FROM permissions WHERE socialid=?',socialid, function(err, rows){
if(err){
console.log(err);
return;
}
callback(rows[0].permission);
});
}
You are using a callback-based approach which is completely fine. You can also use promises. With the callback apparoach, you can return both errors and the result of queries. The first argument can be the error while the second argument can be the returned value
export function getPlayerPermission(socialid, callback) {
connection.query('SELECT permission FROM permissions WHERE socialid=?',socialid,
function(err, rows){
if(err){
console.log(err);
callback(err);
return;
}
callback(null, rows[0].permission);
});
}
Now in your other file, you can call the function as follows
chat.registerCmd('getid', (player, arg) => {
db.getPlayerPermission(player.socialId, function(dbErr, permission) {
// since JS is asynchronous, you need to be in callback function to execute the rest of the code after you get the data from first query
if (dbErr) {
console.log(dbErr);
return;
}
// Now permission variable has data returned from the query
PermissionValue = permission;
var vname;
var name;
if(arg.length <= 1) {
chat.send(player, '/getid [Vorname] [Name]');
return;
}
vname = arg[0];
name = arg[1];
db.getCharIdByName(vname, name, function(charid){
chat.send(player, 'Die ID des Spielers ist: ' + charid);
});
});
});

How can I update a member variable of a Javascript class with a value from MongoDB?

I have a job class, defined in Javascript as such:
var Job = function() {};
module.exports = Job;
I then define a member function using its prototype. That works fine, until I try to use a value from a database to set the member variable:
// Create from an existing job ID
Job.prototype.createFromID = function( inID, callback ) {
// Start by making sure we're invalid
this.id = "";
// Connect to database
var db = MongoClient.connect('mongodb://localhost:27017/nodepurple', function( err, db ) {
if (err) { return false; }
// Find the job document we're interested in
db.collection('jobs').find({ jobID: inID }).limit( 1 ).toArray( function( err, theJobs ) {
if (theJobs.length == 1) {
this.id = theJobs[0].jobID;
// Close the database
db.close();
callback( (this.id != ""), this );
}
}); // Find
}); // Connect
}
The purpose of this function is to:
Get a MongoDB document defining the specific job I'm interested in (defined by the "inID" parameter.
Fill a bunch of member variables of the job instance for which this function was called.
This doesn't work. I think I understand why it doesn't work, when these MongoDB callbacks return, I'm assuming I'm no longer in the right context to make this work, but I'm struggling to see how this can be resolved.
So, how can I take the value MongoDB gives me back for jobID and use that to populate the "id" member variable in the particular Job instance I'm working on?
Yes, you're right, inside the callback this doesn't refer to the Job object anymore. To fix that you have a couple of options.
1) Keep a reference to the Job object to use inside the callback to set the id.
Job.prototype.createFromID = function( inID, callback ) {
this.id = "";
var self = this;
var db = MongoClient.connect('mongodb://localhost:27017/nodepurple', function( err, db ) {
if (err) {
return false;
}
db.collection('jobs').find({ jobID: inID }).limit( 1 ).toArray( function( err, theJobs ) {
if (theJobs.length == 1) {
self.id = theJobs[0].jobID;
db.close();
callback( (self.id != ""), self );
}
});
});
}
2) Bind the Job object to the callback function
Job.prototype.createFromID = function( inID, callback ) {
this.id = "";
var mongoCallback2 = function(err, theJobs) {
if (theJobs.length == 1) {
this.id = theJobs[0].jobID;
db.close();
callback((this.id != ""), this);
}
}
var mongoCallback1 = function(err, db) {
if (err) { return false; }
db.collection('jobs').find({jobID: inID}).limit(1).toArray(mongoCallback2.bind(this));
});
var db = MongoClient.connect('mongodb://localhost:27017/nodepurple', mongoCallback1.bind(this));
}
3) Using arrow functions. Arrow functions do not change the context.
Job.prototype.createFromID = function( inID, callback ) {
this.id = "";
var db = MongoClient.connect('mongodb://localhost:27017/nodepurple', (err, db) => {
if (err) {
return false;
}
db.collection('jobs').find({jobID: inID}).limit( 1 ).toArray((err, theJobs) => {
if (theJobs.length == 1) {
self.id = theJobs[0].jobID;
db.close();
callback((this.id != ""), this);
}
});
});
}
Arrow functions are not supported by all browser or all browser versions but there are tools out there that can convert you code to work on a wider range of browsers and browsers versions.

How we can use promises in node js?

As in asynchronous programming we used to callbacks and promises.
Here I am stuck in a problem that may be used to promises. I google it a lot but there is nothing found that solved my problem.
Here My code that I am doing to send push notification in android device.
router.post('/check-notifications', function(req, res, next) {
var user_id = req.body.user_id;
var response = {};
var gcm = require('push-notify').gcm({
apiKey: gcm_apiKey,
retries: 0
});
connection.query('select device_id from devices where user_id = '+ user_id, function (err, result) {
if ( result.length ) {
for (var i = 0; i < result.length; i++) {
console.log(i + 'before notify');
gcm.send({
registrationId: result[i]['device_id'],
data: result[0]
});
console.log(i + 'before transmitted');
gcm.on('transmitted', function (result, message, registrationId) {
console.log('transmitted');
});
gcm.on('transmissionError', function (error, message, registrationId) {
console.log(message);
});
console.log(i + 'after notify');
}
}
});
response['success'] = true;
response['msg'] = 'sent successfully';
res.json(response);
});
Output :
0before notify
0before transmitted
0after notify
1before notify
1before transmitted
1after notify
transmitted
transmitted
transmitted
transmitted
And I think It should be like this.
0before notify
0before transmitted
transmitted
0after notify
1before notify
1before transmitted
transmitted
1after notify
You can use async.mapSeries method for chaining notifications. Replace for loop to:
async.mapSeries(result, function(item, callback) {
gcm.send({
registrationId: item['device_id'],
data: data
});
gcm.on('transmitted', function(result, message, registrationId) {
console.log('transmitted');
callback(null, message, registrationId);
});
gcm.on('transmissionError', function(error, message, registrationId) {
callback(error, message, registrationId);
});
}, function (err, results) {
if (err) throw err;
response['success'] = true;
response['msg'] = 'sent successfully';
res.json(response);
})
I recommend using Bluebird JS for Promise flow-control.
var Promise = require('bluebird'); // Require bluebird, and call it 'Promise', the code below is version 3.x syntax
var connection = {'query': '???'}; // assuming `connection` is already defined somewhere else
var gcm_apiKey = '???'; // assuming `gcm_apiKey` is already defined
router.post('/check-notifications', function (req, res, next) {
var user_id = req.body.user_id;
var gcm = require('push-notify').gcm({
apiKey: gcm_apiKey,
retries: 0
});
// assuming `connection` is already defined somewhere else
// Make an async version of connection.query
connection.queryAsync = Promise.promisify(connection.query);
connection.queryAsync('select device_id from devices where user_id = ' + user_id)
// Bluebird's Promise.map would execute the following block once per result, asynchronously.
// The sequence of who runs first and who completes first is undefined
.map(function (result, i) {
// the `result` argument here is `result[i]` of the original code, since we're in the map context
// Here we have to create a promise to consume events
return new Promise(function (resolve, reject) {
console.log(i + 'before notify');
gcm.send({
registrationId: result['device_id'],
data: result // original code is written as result[0], which I don't quite understand. Always sending the first result?
});
// This does not make sense console logging here, as it is not actually 'before transmitted'
// It's just binding onto the event
// console.log(i + 'before transmitted');
gcm.on('transmitted', function (result, message, registrationId) {
// Check registrationId
if (registrationId === result['device_id']) {
console.log('transmitted');
resolve(result); // use 'result' as the Promise's resolved value
}
});
gcm.on('transmissionError', function (error, message, registrationId) {
// Check registrationId
if (registrationId === result['device_id']) {
console.log(message);
reject(message); // reject errors and send the message as the promise's reject reason
}
});
// Technically, you should log it as "after event binding"
console.log(i + 'after notify');
});
}).then(function (results) {
// `results` should contain all the result from the 'transmitted' event
var response = {};
response['success'] = true;
response['msg'] = 'sent successfully';
res.json(response);
});
});
Note: The is actually more or less doable without any libraries but with native Promises, but the syntax would be more cluttering.

https.get call within forEach in Node.js

I am doing several https.get calls in a async.waterfall block. In the last function(callback), I have a forEach loop that iterates over the variable I received in the last call and makes GET calls accordingly.
My problem is that when i see in the UI, it does not retrieve all files every time. Sometimes it shows all files but sometimes only partial files. The profile page render happens before all the REST calls are finished and fileData is not fully populated. How can i fix this? Here is my code -
async.waterfall([
function(callback) {
//I do a https.get call here and pass on 2 arguments to the next method
},
function (var1, var2, callback) {
//I do a https.get call here and pass on 2 + 1 arguments to the next method
},
function (var1, var2, var3, callback) {
//Here i do a https OPTIONS call to get all the available REST endpoints and pass those as var4
},
function (var1, var2, var3, var4, callback) {
var fileData = [];
var4.forEach(function(element){
const url = host + var1 + element;
https.get(url, function (n) {
var output = '';
n.on('data', function (chunk) {
output += chunk;
});
n.on('end', function () {
var fileJson = JSON.parse(output);
console.log("FileJSON data here :", fileJson);
if(fileJson) {
if (fileJson.owner == req.user.username) {
fileData.push(fileJson);
}
}
});
if(var4[var4.length - 1] == element){
n.on('end', function () {
res.render('viewProfile',{
title: 'View Profile',
fileData: JSON.stringify(fileData),
events: JSON.stringify(var2),
customerEvents: JSON.stringify(var3)
});
});
callback(null, 'done');
}
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
});
}
], function(err, result){
if(err) return next(err);
});

Getting values within a nest for-loop - node.js, javascript, redis, Q library

I am trying to extract values out of a nested for-loop. My loop takes values from Redis, and I want to add these values to an array variable called "info".
The important bit is the for-loop.
app.get('/query', function(req, res) {
var info = [];
redisClient.keys("todo:*", function(err, data) {
if (err) return console.log(err);
for (var i = 0, len = data.length; i < len; i++) {
var id = data[i];
var listItem, author, goodness;
redisClient.hgetall(data[i], function(err, obj) {
listItem = obj.listItem;
author = obj.author;
goodness = {
id: id,
listItem: listItem,
author: author
}
info.push(goodness);
console.log("In here: " + info);
});
console.log("Out here: " + info);
}
console.log("Way out here: " + info);
});
console.log("WAY THE HECK OUT HERE: " + info);
});
Basically, I want the values in the variable "goodness" to be pushed to an array variable called "info". when I execute the code, the info array gets filled up here,
console.log("In here: " + info);
but I have not found a way to extract the info array to have values outside of the redisClient.hgetall() function. I have tried return statements to no avail, though as a beginning programmer there is a decent chance I'm not doing these properly.
NOTE: I have tried to take guidance from the original answer and I must be doing something wrong, or the solution given wasn't good enough, or both. I have added the Q library to my project, and have tried to find a solution. Here is my current code:
app.get('/query', function(req, res) {
var redisKeys = Q.nbind(redisClient.keys, redisClient);
var redisHGetAll = Q.nbind(redisClient.hgetall, redisClient);
var id, listItem, author;
var info = [];
redisKeys('todo:*').then(function (data) {
console.log("First: " + data);
var QAll = Q.all(data.map(processKeysFunc(info)));
console.log("Reading within this: " + data);
console.log("Next within this: " + QAll);
}, function (err) {
if (err) return console.log(err);
}).then(function () {
console.log("Finally: " + data);
})();
function processKeysFunc(array) {
return function (key) {
console.log("This is the key: " + key);
return redisHGetall(key).then(function (obj) {
console.log("This is the obj: " + obj);
array.push({
id: obj.id,
listItem: obj.listItem,
author: obj.author
});
});
};
}
});
And this is what I get within my console.log:
First: todo:281f973d-6ffd-403b-a0f4-9e8958caed35,todo:7ed8c557-0f15-4555-9119-
6777e1c952e8,todo:eb8dbee1-92ca-450e-8248-ad78548cd754,todo:712e8d27-bf9b-46f0-bfdd-
c53ef7d14441,todo:301dd91a-2b65-4b87-b129-a5ad569e38e5,todo:720d98b8-bdec-446d-a178-
fb7e264522aa,todo:d200c6cf-2ee5-443b-b7dc-d245c16899c8,todo:8169e9af-0204-42c8-9ddf-
3b00f7161b11
This is the key: todo:281f973d-6ffd-403b-a0f4-9e8958caed35
node.js is generally non-blocking, this is why callbacks are used. The callback passed to .hgetall will only execute when the data from redis has been fully received. The rest of the code around it will execute immediately and not wait for the data from redis. In fact, since the .hgetall call likely involves IO the callback will always run after the code around has executed.
You have a few options, including using Promises (https://github.com/kriskowal/q).
I'll suggest a solution that should be comprehensible the current structure of your code:
app.get('/query', function(req, res) {
var info = [];
var completed = 0;
redisClient.keys("todo:*", function(err, data) {
if (err) return console.log(err);
for (var i = 0, len = data.length; i < len; i++) {
var id = data[i];
var listItem, author, goodness;
redisClient.hgetall(data[i], function(err, obj) {
completed++;
if (err) return console.log(err);
listItem = obj.listItem;
author = obj.author;
goodness = {
id: id,
listItem: listItem,
author: author
}
info.push(goodness);
console.log("In here: " + info);
if (completed === data.length) {
// Do something with info here, all work has finished
}
});
console.log("Out here: " + info);
}
console.log("Way out here: " + info);
});
console.log("WAY THE HECK OUT HERE: " + info);
});
The key bit is the new variable completed that tracks how many callbacks have returned.
I would highly recommend using promises instead, though. Something like:
var Q = require('q');
var redisKeys = Q.nbind(redisClient.keys, redisClient);
var redisHGetall = Q.nbind(redisClient.hgetall, redisClient);
app.get('/query', function(req, res) {
var info = [];
redisKeys('todo:*').then(function (data) {
return Q.all(data.map(processKeysFunc(info));
}, function (err) { /* handle error */ }).then(function () {
console.log('Complete info array=%j', info);
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(info));
});
});
function processKeysFunc(array) {
return function (key) {
return redisHGetall(key).then(function (obj) {
var goodness = {
id: key,
listItem: obj.listItem,
author: obj.author
};
array.push(goodness);
});
}
}
processKeysFunc returns a function so that you can cleanly pass the info array around without needing to define every function inline. If you prefer inline, that works too though.

Categories

Resources