NodeJS Change variable from within a function [duplicate] - javascript

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
Long story short, im newish to NodeJS and im trying to change a variable after a function has been executed, however I cant return a result like I thought I could.
app.get('/:steamid', function(req, res) {
var steamID;
var steamAvatarUrl;
var steamRegisterDate;
steamAPI.ResolveVanityURL(req.params.steamid, function(err, res) {
console.log('Steam64: ' + res); // <- this prints out my result
steamID = res;
});
steamAPI.getAvatarUrl(req.params.steamid, function(err, res) {
console.log('Avatar URL: ' + res); // <- this prints out my result
steamAvatarUrl = res;
});
steamAPI.memberSince(req.params.steamid, function(err, res) {
console.log('Registered: ' + res); // <- this prints out my result
steamRegisterDate = res;
});
console.log(steamID); //<--- This returns undefined
res.render('steamid.hbs', {
"title": steamID,
"avatarURL": steamAvatarUrl,
"registerDate": steamRegisterDate
});
});
An help with an example/resolution of how I accomplish this would be amazing. I am a visual learner so please dont say "use this or do that" without giving some sort of example to it or I honestly wont get it lol.
Thanks ahead of time.

Javascript is Async in nature, so before it fetches the data you are trying to print it. You can write console.log() inside your callback function.
or please use promises.

You can solve this problem using Promise. Your api call is async. So it will not wait to complete the api call. it just executes console.log(). That's why you are getting undefined. I'm not sure about your node version. if promises are not available you could use bluebird library.
app.get('/:steamid', function(req, res) {
var steamID;
new Promise(function(resolve, reject){
steamAPI.ResolveVanityURL(req.params.steamid, function(err, res) {
resolve(res);
});
}).then(function(res){
steamID = res;
console.log('Steam64: ' + steamId);
}
});

Javascript is asynchronous by default. You can't return a value inside of a callback function neither can you assign to a variable outside as it is returned immediately.
app.get('/:steamid', function(req, res) {
steamAPI.ResolveVanityURL(req.params.steamid, function(err, res) {
console.log('Steam64: ' + res); // <- this prints out my result
var steamID = res;
console.log(steamID); // <-- defined
});
});
You could learn about Promises and how they work as they are preferred to using callbacks.

Javascript is asynchronous in nature. The function 'steamAPI.ResolveVanityURL()' is taking time to execute and therefore next statement 'console.log(steamID)' is getting executed before waiting for the result.
You can either use callbacks or below is an example using 'q' promise library to solve your problem -
var Q = require('q');
app.get('/:steamid', function(req, res) {
var steamID;
resolveUrl(req.params.steamid).then(function(steamID) {
console.log(steamID); //<--- This will return steamID
(...)
});
});
function resolveUrl(id) {
var deferred = Q.defer();
steamAPI.ResolveVanityURL(id, function(err, res) {
console.log('Steam64: ' + res); // <- this prints out my result
deferred.resolve(res);
});
return deferred.promise;
}
Also here is a link to get more information about promises in node.js -
http://www.dotnetcurry.com/nodejs/1242/promises-nodejs-using-q-goodbye-callbacks
Hope this solves your problem.

#Zenderx let breif this .
Node.js is Async programming , if you do any I/O event like database call ,http , timeout call .
It send then in event pool and execute in sequence .
See the comment what happens step by step.
What is happing in your code it this.
This is your api call
app.get('/:steamid', function(req, res) {
// excution comes inside
var steamID;
// then you call an I/O event and iniallized streamID but what happen here is this ,
//this is I/O event so went to event loop for excution
steamAPI.ResolveVanityURL(req.params.steamid, function(err, res) {
console.log('Steam64: ' + res); // <- this prints out my result
steamID = res;
});
// while the block is in event loop or excution this statement run because of Async nature
console.log(steamID); //<--- This returns undefined
(...)
});
if you want to print StreamID print it inside the block only .
steamAPI.ResolveVanityURL(req.params.steamid, function(err, res) {
console.log('Steam64: ' + res); // <- this prints out my result
steamID = res;
console.log(steamID);
});
Too study in depth of async , you can look to promise and async library in node.js .
The simplest thing to do for newbie is this .
app.get('/:steamid', function(req, res) {
var steamID;
var steamAvatarUrl;
var steamRegisterDate;
steamAPI.ResolveVanityURL(req.params.steamid, function(err, resu) {
console.log('Steam64: ' + resu); // <- this prints out my result
steamID = resu;
steamAPI.getAvatarUrl(req.params.steamid, function(err, resu) {
console.log('Avatar URL: ' + resu); // <- this prints out my result
steamAvatarUrl = resu;
steamAPI.registerDate(req.params.steamid, function(err, resu) {
console.log('Registered: ' + resu); // <- this prints out my result
steamRegisterDate = resu;
console.log(steamID); //<--- This returns undefined
res.render('steamid.hbs', {
"title": steamID,
"avatarURL": steamAvatarUrl,
"registerDate": steamRegisterDate
});
});
});
});
});
If you need it to be solve by promise and async module ping.

Related

Node.js cannot pass string into res.send(); Undefined

I'm a Node.js newbie trying to send back data to the browser by accessing a function load_blocks() in an external file I wrote and call it with res.send().
I have two simple files:
The first one is the typical app.js file:
const express = require('express');
const app = express();
//import my file
const blockchain = require('./load_blockchain.js');
app.get('/', function(req, res){
res.send('<h3>' + blockchain.load_blocks() + '</h3>');
});
app.listen(3000, function(){
console.log('Example app listening on port 3000!');
});
load_blockchain.js
This file has a function load_blocks() which is meant to read the lines of a file and then return them as text. But when I load my page, the <h3> tag reads undefined
var fs = require('fs');
var readline = require('readline');
module.exports = {
load_blocks: function (){
//Load The File
var return_string = "";
var rd = readline.createInterface({
input:fs.createReadStream('/home/blockchain_data/1.dat$
output: process.stdout,
console: true
});
rd.on('line', function(line){
console.log(line);
return_string += line;
});
return return_string;
}
};
Shouldn't the h3 tag read the data that is in the file I passed now? What is going wrong?
load_blocks() is asynchronous. It does not return the finished string because the readline code inside that function is asynchronous and has not yet finished when load_blocks() returns so thus return_string does not yet have the value you want when you return.
This is a classic asynchronous programming problem in Javascript. Your load_blocks function needs to either return a promise or call a callback when it finishes its work and the caller has to use that returned promise or callback to obtain the final result.
For a lot more info on returning an asynchronous value, see the various options offered here: How do I return the response from an asynchronous call?
You could change the code to something like this:
app.get('/', function(req, res){
blockchain.load_blocks().then(val => {
res.send('<h3>' + val + '</h3>');
}).catch(err => {
console.log(err);
res.status(500).end();
});
});
And, then change blockchain.load_blocks() to return a promise that resolves when you have the final value.
The code you show for load_blocks appears to just be reading an entire file. I don't understand any reason that you are reading it line by line. If you just want to read a whole file, you can just use fs.readFile() to read the whole file.
module.exports = {
load_blocks: function() {
return new Promise((resolve, reject) => {
fs.readFile('/home/blockchain_data/1.dat$', function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
};

Function Return not Working

I am working in Node and trying to load the next sequence from my db. I am able to access the db, load and return the sequence within my function, but I am not able to access it outside of the function.
function getRunId() {
counters.findOne({_id: 'Run_ID'}, function(err, resp) {
if(err) {
console.log(err);
}
console.log('Seq: ' + resp.sequence); // Console Output = Seq: 1234
return resp.sequence;
});
};
var currentRunId = getRunId();
console.log('Run_ID: ' + currentRunId); // Console Output = CRID: undefined
I've checked several pages worth of Stack Overflow issues relating to using callback's, async (node module), how to properly return values in the function, etc... but none of them get me closer to accessing currentRunId outside of the function.
Is this issue further complicated by the use of Mongo queries inside my function?
For anyone stumbling on this later, start by reading this answer.
I've dealt with this a few times so I understand the frustration. You are trying to mix sync and async code by doing this:
var currentRunId = getRunId();
console.log('Run_ID: ' + currentRunId);
The trouble is that console.log('Run_ID: ' + currentRunId) is called immediately after you invoke getRunID() by assigning it to current RunID, and getRunID() resolves after console.log('Run_ID: ' + currentRunId), causing the currentRunId variable to be undefined.
But, you have some options to deal with this. Option one is to return a callback, and log the results of the callback instead. Option 2 is to use an ES6 promise. To use option 2, you need node version 7, and you need to use 'use strict' in your code.
Here are 3 examples built around a function stub that spoofs the results of findOne(). The getRunIdA() is your function, and getRunIdB, and getRunIdC are two example solutions to your current problem.
'use strict'
// A function stub which represents a simplified version of findOne.
// Accepts callback and returns a callback with the results of data
function findOne (callback) {
var data = {
sequence: 6
}
return callback(null, data)
}
// This is a simplified version of your function, which reproduces the undefined result
function getRunIdA () {
findOne(function (err, resp) {
if (err) {
console.log(err)
}
console.log('Seq: ' + resp.sequence)
return resp.sequence
})
}
// This is your function with a callback
function getRunIdB (callback) {
findOne(function (err, resp) {
if (err) {
console.log(err)
}
console.log('Seq: ' + resp.sequence)
return callback(resp.sequence)
})
}
// This is your function with a promise
var getRunIdC = new Promise(function (resolve, reject) {
resolve(findOne(function (err, resp) {
if (err) {
console.log(err)
}
return resp.sequence
}))
})
// Invoke your funciton; get undefined
var currentRunID = getRunIdA()
console.log('Run_ID: ' + currentRunID) // Run_ID: undefined
// Invoke getRunIdB using callback, get 6
getRunIdB(function (id) {
console.log('Run_ID: ' + id) // Run_ID: 6
})
// Invoke getRunIdC with a promise; get 6
getRunIdC.then(function (currentRunID) {
console.log('Run_ID: ' + currentRunID) // Run_ID: 6
})
/*
results for all 3:
Seq: 6
Run_ID: undefined
Seq: 6
Run_ID: 6
Run_ID: 6
*/
Give this a try by saving to your machine and running:
node test.js
Is this issue further complicated by the use of Mongo queries inside my function?
Nope, you just need to pass the results of your query to a promise or a callback so that you can work with the results somewhere else.
I hope this helps!
Edit: OP added the following code in a comment, which I will try to break down and address.
Unfortunately, using getRunIdB results in callback is not defined and using getRunIdC results in currentRunId is not defined
var currentRunID = '';
var getRunId = new Promise(function (resolve, reject) { resolve(counters.findOne({_id: 'Run_ID'}, function (err, resp) {
if (err) {
console.log(err)
}
return resp.sequence;
}))
});
getRunId.then(function (res) {
console.log('Run_ID: ' + res.sequence) // Run_ID: 1234
currentRunID = res.sequence;
})
console.log(currentRunID); // currentRunID is not defined
Check out an answer I gave to a similar question for more details on the JS concurrency model. Simply put, the getRunID() function is executing asynchronous code. What that means is that getRunID() doesn't get inserted into the message queue that determines what order javascript will execute until it's callbacks are completed. Thus, when you log currentRunID outside of the .then() function, the results is undefined because currentRunID is undefined.
I think that ultimately what OP is trying to do is to export the result of the function so that the something can be done with those results, this needs to be done within a callback like so:
getRunId.then(function (res) {
// Do stuff with the run ID here.
})
You are only returning on a callback function but not on the actual function.. Change your code to this:
function getRunId() {
var result = counters.findOne({_id: 'Run_ID'}, function(err, resp) {
if(err) {
console.log(err);
}
console.log('Seq: ' + resp.sequence); // Console Output = Seq: 1234
return resp.sequence;
});
return result; //<-- return result of your function is here
};
var currentRunId = getRunId();
console.log('Run_ID: ' + currentRunId);

Return the result of a mongoose model function

I'm working on a Restful API with node.js and mongoose. I need to return an object with the results of a search. Here is my code:
var Get = function (criteria) {
var result;
Users.findOne(criteria, function(err, res){
console.log('Error: ' + err);
console.log('Results: ' + res);
result = err || !res ? err || errors['404']: res;
console.log(result);
});
console.log('Final results: ' + JSON.stringify(result));
return result;
};
Since I was having problems, I added those console.logs to watch where the result is lost. Here are the logs:
Final results: undefined
Error: null
Results: { //user... }
I need that result to be returned. How can I solve this?
This is one of the most asked questions in the history of the internet!
Since node is asynchronous you'll need to rewrite the function to return a callback or use something like promises.
Here it is with callbacks:
var Get = function (criteria, cb) {
return Users.findOne(criteria,cb);
};
And then call it like:
Get({}, function(err, res){
console.log(res);
});
Or you can use promises, here it is using the Q library
var Get = function (criteria) {
var deferred = Q.defer();
Users.findOne(criteria,function(err, o){
if (err) deferred.reject(err);
deferred.resolve(o);
});
return deferred.promise;
}
And then call it like this:
Get({}).then(function(res){console.log(res)});
The promise based version seems more complicated to me :).

Using JS Promises to Execute Multiple Async Queries to Build an Object

After recently discovering JS promises, I have been studying them so that I might build a certain functionality that allows me to execute 4 async queries, use the result of each to build an object that I can finally send as a response to a request directed at my node app.
The final object is made up of 3 array properties containing the resulting rows of each query.
It seems that I've done something wrong handling the promises, though, because ultimately, game is not being built. It is sent as an empty object. Here's a JSFiddle.
What is my mistake?
Here's what I have so far:
function sendGame(req, res, sales, settings, categories) {
var game = new Object();
game.sales = sales;
game.settings = settings;
game.categories = categories;
JSONgame = JSON.stringify(game);
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://localhost',
'Content-Length': JSONgame.length,
'Content-Type': 'application/json'
});
res.write(JSONgame);
res.end();
console.log('Game: ' + JSON.stringify(game, null, 4));
console.log('--------------------------------------');
console.log('User ' + req.body.username + ' successfully retrieved game!');
}
function retrieveSales(req, connection, timeFrame) {
console.log('User ' + req.body.username + ' retrieving sales...');
connection.query('select * from sales_entries where date BETWEEN ? AND ?', timeFrame,
function (err, rows, fields) {
if (err) {
callback(new Error('Failed to connect'), null);
} else {
sales = [];
for (x = 0; x < rows.length; x++) {
sales.push(rows[x]);
}
//console.log('Sales: ' + JSON.stringify(sales, null, 4));
return sales;
}
});
}
retrieveCategories() and retrieveSettings() omitted for readability; they are the same as retrieveSales() mostly.
function gameSucceed(req, res) {
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).format("YYYY-MM-DD HH:mm:ss")];
var connection = createConnection();
connection.connect(function (err) {
if (err) return callback(new Error('Failed to connect'), null);
console.log('Connection with the Officeball MySQL database openned for game retrieval...');
var sales = retrieveSales(req, connection, timeFrame);
var settings = retrieveSettings(req, connection);
var categories = retrieveCategories(req, connection);
var all = q.all([sales, settings, categories]);
all.done(function () {
sendGame(req, res, sales, settings, categories);
});
});
}
Your problem is that you're not using promises. All your APIs use callbacks.
A promise is like a closed box:
A promise also has a method that opens the box, works on the value and returns another box on the value (also opening any additional boxes along the way). That method is .then:
In boxes, it does:
=>( . => ) =>
That is, it adds a handler that gets an open box and returns a box. Everything else just combines stuff. All .all does is wait for a list of promises to resolve, it is exactly like .then in the fact it waits for a result. Because promises are boxes, you can pass them around and return them which is very cool.
Generally:
Whenever you return from a promise handler (not a rejection), you are fullfilling it indicating normal flow continuation.
Whenever you throw at a promise handler, you are rejecting indication exceptional flow.
So basically in node speak:
Whenever you returned a null error and a response, you resolve the promise.
Whenever you returned an error and no response, you reject the promise.
So:
function myFunc(callback){
nodeBack(function(err,data){
if(err!== null){
callback(new Error(err),null);
}
callback(data+"some processing");
})
});
Becomes:
function myFunc(){
return nodeBack().then(function(data){ return data+"some processing"; });
}
Which I think is a lot clearer. Errors are propagated across the promise chain just like in synchronous code - it's very common to find synchronous analogs to promise code.
Q.all takes a list of promises and waits for them to complete, instead you want Q.nfcall to transform a callback based API to a promise one and then use Q.all on that.
That is:
var sales = Q.nfcall(retrieveSales,req, connection, timeFrame);
var settings = Q.nfcall(retrieveSettings,req, connection);
var categories = Q.nfcall(retrieveCategories, req, connection);
Q.nfcall takes a nodeback in the err,data convention and converts it to a promise API.
Also, when you do
return sales;
You are not really returning anything, since it returns synchronously. You need to use callback like in your error case or promisify it altogether. If you don't mind, I'll do it with Bluebird since it comes with much better facilities for dealing with these interop cases and does so much much faster, if you'd like you can switch promisifyAll for a bunch of Q.nfcall calls.
// somewhere, on top of file
connection = Promise.promisifyAll(connection);
// note I'm passing just the username - passing the request breaks separation of concerns.
var retrieveSales = Promise.method(username, connection, timeFrame) {
console.log('User ' + username + ' retrieving sales...');
var q = 'select * from sales_entries where date BETWEEN ? AND ?';
return connection.queryAsync(q, timeFrame).then(function(rows, fields){
return rows;
});
}
Note that suddenly you don't need a lot of boilerplate for making a query, you can use queryAsync directly instead if you'd like.
Now the code that wraps it becomes:
var gameSucceed = Promise.method(function gameSucceed(req, res) {
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment()....];
var connection = Promise.promisifyAll(createConnection());
return conn.connectAsync().then(function () {
console.log('Connection with the ...');
//sending req, but should really be what they use.
return Promise.all([retrieveSales(req,conn,timeFrame),
retrieveSettings(req,conn),
retrieveCategories(req,conn)]);
});
});
Now you can call sendGame(req, res, sales, settings, categories); outside of gameSucceed which doesn't hide what it does as much -
gameSucceed(req,res).spread(function(sales,settings,cats){
return sendGame(req,res,sales,settings,cats);
});

Node.js Variable scope

I have a http server setup which basically needs to look up stuff in the database.
Here is the code snippet :
var sys = require('sys');
var Client = require('mysql').Client;
var client = new Client();
client.host = '_';
client.user = '_';
client.password = '_';
client.database = '_';
var http = require('http');
http.createServer(function(req, res) {
req.on('end', function() {
client.connect(function(error, results) {
if (error) {
console.log('Connection Error:');
return;
}
ClientConnectionReady(client);
});
ClientConnectionReady = function(client) {
var final = '';
client.query('select * from table', function selectCb(error, result, fields) {
if (error) {
console.log('ERROR');
client.end();
return;
}
final += "{" + JSON.stringify(result);
});
client.query("SELECT COUNT(*) from table", function selectCb(error, result, fields) {
if (error) {
console.log('ERROR');
client.end();
return;
}
final += "," + JSON.stringify(result) + "}";
});
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.write(final);
res.end();
client.end();
};
});
}).listen(8007, "127.0.0.1");
  
If I print the values of the variable 'final' at the places where I assign them, I see valid values, but at the lines when I do 'res.write(final)', final is still blank.
How do I make this work and why is this failing?? Thanks for the help, I am new to node.js
The Node.js environment is asynchronous. Those statements that modify "final" are inside callbacks that are executed only when the database operations finish. The code immediately after the initiation of the database operations, where you write the result, are executed long before those callbacks run.
You've almost stumbled upon the answer to the problem already: you must not write the result until the operations are finished, which you know will be the case inside the callbacks. If you must wait for both to finish (seems like you do), then you can do something like keep a counter in the outer scope. Each callback can increment the counter, and call the same result-writer function only when the counter indicates that both callbacks are complete. (I have the idea that the Node runtime has a fancier way of doing that sort of thing, but I'm not that familiar with it. In a simple case like this, keeping something like a counter is easy enough to do.)
Also, an unrelated note: that "ClientConnectionReady" variable should probably either be written as a function definition:
function ClientConnectionReady(client) {
// ...
}
or else it should be declared with var. (I'm a little surprised in fact that it's not throwing an error, but again I'm not that familiar with Node.js.)
By the looks of it, you are trying to write final before it is ever assigned a value.
I'm assuming that client.query is asynchronous. Given that, the callback function is most likely being called after the res.writeHead and res.write lines. What you need to do is put other calls and the client.write* lines within the first callback.
This should give you an idea (didn't check if it compiles)
ClientConnectionReady = function(client)
{
var final = '';
//Get the rows
client.query('select * from table',
function selectCb(error, result, fields)
{
if (error)
{
console.log('ERROR');
client.end();
return;
}
final+="{"+JSON.stringify(result);
//Get the count query
client.query("SELECT COUNT(*) from table",
function selectCb(error, result, fields)
{
if (error)
{
console.log('ERROR');
client.end();
return;
}
final+=","+JSON.stringify(result)+"}";
//Return the final results to the client now
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(final);
res.end();
client.end();
});
});
};
What this does is first gets the rows. In that callback, it then gets the count. Finally, when that works, it sends the data to the client within the count callback.

Categories

Resources