Declared array returning undefined - node.js

In my project, one of the functions should update a list of users with new stats,and I have that function:
function gameEnded(team){
//reset variables
playersObject = {};
isPlaying = false;
subQueue = {};
subArray = [];
blueList = ["Jonas","LucasTT"];
redList = ["Lucas","Manelzao"];
//updates database
for(i=0; i<blueList.length; i++){
getPlayerStats(blueList[i], function(oldStats){
console.log(blueList[i]);
setPlayerStats(blueList[i], [oldStats[0]+6,oldStats[1]]);
});
}
}
It should get the a name from the list, get the name's stats(using MySQL),and then update it. But, the console.log there is logging undefined, but the array is declared. What is causing that to do so?

The problem is that the callback doesn't run until after that loop has finished, at which point i is equal to blueList.length, and in javascript indexing past the end of an array returns undefined.
You'll want to freeze the value of i in each iteration of the loop, which can be done with an IIFE:
for(i=0; i<blueList.length; i++){
(function(i) {
getPlayerStats(blueList[i], function(oldStats){
console.log(blueList[i]);
setPlayerStats(blueList[i], [oldStats[0]+6,oldStats[1]]);
});
})(i);
}

Related

how can call another function after excuted multiple function in loop node js

Please find below code
function get_btc(address) {
address_transaction(address, user_id, coin_key, deposite_txn_fee, function(callback) {
for (var j = 0; j < callback.response.data.txs.length; j++) {
let user_id = callback.user_id;
//some code//
}
});
}
get_label_info(function(err, data) {
for (var i = 0; i < data.length; i++) {
let address = data[i].address;
deposite_model.get_coin_info(function(err, data1) {
var coin_name = data1[0].coin_code;
const return_functions = get_switch(coin_name);
if (return_functions) {
obj[return_functions](address);
}
})
}
});
function all_completed() {
console.log('all functions has been completed');
}
By the help of above mentioned code i want to excecute all_completed loop when all functions has been completly done.
At the initial start get_label_info function is excuted then controller go on to get_btc function.
Please help me how could i run all_completed functions after all functions completed run.
I'll assume you are using es6, and that you know what a Promise is in that context. In that case wrap all your callback based things in a Promise that resolves when the callback completes. Then, in your loop, push all your Promises into an array variable. Finally call Promise.all with that array as an argument and call then on the result to encapsulate the code you want ti run after they all complete (resolve).

value changed to undefined when coming outside the loop

The value of rows[i] is perfectly printing inside the loop, but when we print it outside it becomes undefined.I don't want to use set timeout function in my program.
function listshops(callback)
{
client.connection.query('select * from shop',function(err,rows){
if(rows.length>0)
{
for(var i=0;i<rows.length;i++)
{
(function(i){
var shopIdFetched = rows[i].shopId;
client.connection.query('select * from image where shopId=?',shopIdFetched,function(err,data){
if(data.length > 0){
var result = rows[i].image = JSON.stringify(data);
}
});
})(i);
}
console.log(rows[i]);
}
});
}
output:
First you are getting undefined cause your i in rows[i] is out of index. But solving that wont solve your problem as you are performing multiple asynchronous task within a for loop. Your rows object will not be populated by the time you print it.
Solution: You need to use async or promises for performing the task.
// Include the async package
var async = require("async");
client.connection.query('select * from shop',function(err,rows){
if(rows.length>0)
{
// 1st para in async.each() is the array of items
async.each(rows,
// 2nd param is the function that each item is passed to
function(item, callback){
// Call an asynchronous function,
var shopIdFetched = item.shopId;
client.connection.query('select * from image where shopId=?',shopIdFetched,function(err,data){
if(data.length > 0){
item.image = JSON.stringify(data);
}
callback();//required
});
},
// 3rd param is the function to call when everything's done
function(err){
if(err){
console.log('Error:' + err);
}
console.log(rows);// your result
}
);
}
});
When you exit the loop, the value of i is equal to the array's length.
Let's say the length is 10, so indexes run from 0 to 9.
So in fact, after the loop, rows[i] is rows[10], which is indeed undefined.

undefined value returning while executing sql query in nodejs [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Javascript infamous Loop issue? [duplicate]
(5 answers)
Closed 6 years ago.
I am fetching values from my database having tables:
My code:
function listshops(callback)
{
client.connection.query('select * from shop',function(err,rows){
if(rows.length>0)
{
for(var i=0;i<rows.length;i++)
{
var shopIdFetched = rows[i].shopId;
client.connection.query('select * from image where shopId=?',shopIdFetched,function(err,data){
if(data.length > 0){
console.log(rows[i],data);
}
});
}
}
});
}
But when displaying result, the first query shows a undefined value.
When i gives rows [0] and rows1 the values are fetching. But i need to implement rows[i].
You misunderstand how asynchronous calls are make.
What happens when you run this part of code?
for(var i=0;i<rows.length;i++)
{
var shopIdFetched = rows[i].shopId;
client.connection.query(...) //these are asynchronous methods
}
For rows.length=10, it will call 10 times the client.connection.query, which is unfortunately asynchronous method, therefore it is not executed yet, but it put 10 asynchronous methods to Event Stack.
After this method synchronously finishes and one of method indicates, the call to database is finished, the method is executed, which is this
if(data.length > 0){
console.log(rows[i],data);
}
However at this point, the for-cycle already finished, the i=10, therefore rows[10] is undefined (because for rows.length=10 you have data in rows[0] to rows[9]
One workaround can be to put another method to the inner scope, something like this
for(var i=0;i<10;i++)
{
x(i);
}
function x(i){
console.log(i);
//this i will be same even after asynchronous paradighm
}
The same thing can be written as this
for (var i = 0; i < 10; i++) {
(function(i){
console.log(i);
})(i)
}
In your case
for(var i=0;i<rows.length;i++)
{
(function(i){
var shopIdFetched = rows[i].shopId;
client.connection.query('select * from image where shopId=?',shopIdFetched,function(err,data){
if(data.length > 0){
console.log(rows[i],data);
}
});
})(i);
}
For better understanding, this would do the same
for(var index=0;index<rows.length;index++)
{
(function(i){
var shopIdFetched = rows[i].shopId;
client.connection.query('select * from image where shopId=?',shopIdFetched,function(err,data){
if(data.length > 0){
console.log(rows[i],data);
}
});
})(index);
}
In previous example, we just shadowed variable i with different variable i (if more variables with same name are created, the one which is in the most inner scope will be selected)
You can not rely on i in async callbacks, because it was changed at time handler called.
You should to create some scope to save iteration data (i or row).
With Array.prototype.forEach:
rows.forEach(row => {
var shopIdFetched = row.shopId;
client.connection.query('select * from image where shopId=?',shopIdFetched,function(err,data){
if(data.length > 0){
console.log(row,data);
}
});
});
With IIFE:
for (var i=0; i<rows.length; i++) {
!function(i) {
// here you can use `i`/`rows[i]` without initial issue
}(i);
}

Get the result at the right time with node

i am new to node.js, i followed some tutorial.
I don't know how to get the array "result" only when the call in the function end.
app.get('/api/email/check/:email',function (request,response){
var email = request.params['email'];
var result = Array();
for (var i = 0; i < 2; i++) {
existence.check(email, function(err,res){
result[i]=res; console.log({"Result":res});
});
};
response.send(result); // Problem is that i get: []
});
I got the log but the result is an empty array because it's called before the functions ends. Is there a nice way to resolve this ? without counting the "i".
You can put
response.send(result);
out of the for loop.
Because response.send() is a async method so before the for loop ends, response has ended before.

http call in backbone promise

Hi I have a backbone web app using Jquery and NodeJs/mongo as the server side framework. I'm having problems with making a http get call with a foreah loop and the results of the get call being iteratively added to each row of the loop.
var eventid = this.model.get("_id");
var inPromise = $.get("/registrants/list?eventid="+eventid,null,null,"json").then(
function (result){
var temp;
var finalVal = '';
var tempfinalVal = "";
var loop = 0
percentage = 0;
$.each(result.registrants,function(index,registrant){
temp = JSON.parse(registrant.fields);
for (var key in temp) {
if(key =="Email"){
if(temp[key] != ""){
$.get("/stats/registrant?userid="+temp[key]+"&eventid="+eventid,null,null,"json").then(function(result2){
percentage = (result2.Stats.type ===undefined || result2.Stats.type ==null) ? "0": result2.Stats.type;
finalVal +=percentage+"\n";
}).fail(function(){
percentage = "0";
});
}
}else if(key =="eventid"){
loop++;
finalVal = finalVal.slice(0, - 1);
finalVal +='\n';
}
finalVal +=temp[key] + ',';
}
});
//promises.push(inPromise);
}
).done(function(finalVal){
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(finalVal));
console.log("DONE");
}).fail(function(){
console.log("fail");
});
// promise.done(function () {
// console.log(" PROMISE DONE");
// });
So I have the loop through a collection and the last item of the docuemnt gets a content froma nother http call and when all is fone it will create a CSV file. The problem is that THE "DONE" text echos firts then the "CALL" text is displayed
Rick, your problem is not the simplest due to :
the need for nested asynchronous gets
the need to build each CSV data row partly synchronously, partly asynchronously.
the need for a mechanism to handle the fulfilment of multiple promises generated in the inner loop.
From what you've tried, I guess you already know that much.
One important thing to note is that you can't rely on for (var key in temp) to deliver properties in any particular order. Only arrays have order.
You might try something like this :
var url = "/stats/registrant",
data = { 'eventid': this.model.get('_id') },
rowTerminator = "\n",
fieldNames = ['firstname','lastname','email','company','score'];
function getScore(email) {
return $.get(url, $.extend({}, data, {'userid':email}), null, "json").then(function(res) {
return res.Stats ? res.Stats.type || 0 : 0;
}, function() {
//ajax failure - assume score == 0
return $.when(0);
});
}
$.get("/registrants/list", data, null, "json").then(function(result) {
var promises = [];//An array in which to accumulate promises of CSV rows
promises.push($.when(fieldNames)); //promise of CSV header row
if(result.registrants) {
$.each(result.registrants, function(index, registrant) {
if(registrant.fields) {
// Synchronously initialize row with firstname, lastname, email and company
// (omitting score for now).
var row = fieldNames.slice(0,-1).map(function(fieldName, i) {
return registrant.fields[fieldName] || '';
});
//`row` remains available to inner functions due to closure
var promise;
if(registrant.fields.Email) {
// Fetch the registrant's score ...
promise = getScore(registrant.fields.Email).then(function(score) {
//... and asynchronously push the score onto row
row.push(score);
return row;
});
} else {
//or synchronously push zero onto row ...
row.push(0);
//... and create a resolved promise
promise = $.when(row);
}
promises.push(promise);//Accumulate promises of CSV data rows (still in array form), in the correct order.
}
});
}
return $.when.apply(null, promises).then(function() {
//Join all the pieces, in nested arrays, together into one long string.
return [].slice.apply(arguments).map(function(row) {
return row.join(); //default glue is ','
}).join(rowTerminator);
});
}).done(function(str) {
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(str));
console.log("DONE");
}).fail(function() {
console.log("fail");
});
partially tested
See comments in code for explanation and please ask if there's anything you don't follow.

Resources