Can't read values that are outside of function - javascript

I am learning JavaScript and AngularJS. I want to use the values that are outside of function, but I don't know how to access them.
Here is my code (AngularJS Controller):
var init = function() {
$http.get('getSomeValues').then(function (res) {
var returnArray = res.data; // Result is array
for(var i=0; i < returnArray.length; i++) { // Loop through the array
console.log("THIS WORKS FINE: ", returnArray[i].value); // It works
$http.get('getOtherValues/' + returnArray[i].value).then(function (res) {
console.log("WHAT'S IN IT: ", returnArray[i].value); // Shows 'undefined' error
});
}
});
};
init();
So basically I want to access the array returnArray, but I can't. Is there any good way to access the values? I assume that '.then(function ..' causes error..

You'll need to use a IIFE:
Replace:
for(var i=0; i < returnArray.length; i++) { // Loop through the array
$http.get('getOtherValues/' + returnArray[i].value).then(function (res) {
console.log("WHAT'S IN IT: ", returnArray[i].value); // Shows 'undefined' error
});
}
With:
for(var i=0; i < returnArray.length; i++) { // Loop through the array
(function(data){
$http.get('getOtherValues/' + data.value).then(function (res) {
console.log("WHAT'S IN IT: ", data.value); // Shows 'undefined' error
});
}(returnArray[i]))
}
This ensures that, for the current iteration of the for loop, the data variable will be set to the current raturnArray item.

Related

JS Array allways returns undefined and length = 0

(using Node.js)
Hi, I have an array with users (User class) on it, when I print the array with console.log, it shows the content correctly and shows that it's length is 3, but when i try to get any thing from the array, it returns undefined and for *.length, it returns 0. Where's the problem?
exports.users = [];
exports.loadUsers = (callback) => {
let more = true;
let i = 0;
while(more) {
let us = _usersFolder + "us_" + i + "/";
if(fs.existsSync(us)) {
fs.readFile(path.join(us + "personal.json"), (err, data) => {
if(err) {
console.log("failed to load file!");
return;
}
let json_personal = JSON.parse(data);
this.users.push(new User(json_personal));
});
i++;
} else {
more = false;
}
}
callback();
}
exports.getUserById = (id) => {
console.log(this.users);
console.log("length: " + this.users.length);
console.log(this.users[0]);
for(let i = 0; i < this.users.length; i++) {
let u = this.users[i];
console.log(u.id);
if(u.id === id) {
return u;
}
}
return false;
}
getUserById is called in the callback, so users are already loaded.
It depends on where you are using the 'this' object. It's possible that 'this' makes reference to a different object than the one you stored the array in ('this' varies depending on the scope where you are using it).
I'd need more information to help you.
var users=[{"num":1},{"num":2},{"num":3}];
console.log(this.users);
console.log("length: " + this.users.length);
console.log(this.users[0]);
output
(3) [Object, Object, Object]
length: 3
Object {a: 1}
I hope you are defining after you print in console.
var users = [];
console.log(users);
console.log("length: " + users.length);
console.log(users[0]);
users.push(1);
users.push(2);
users.push(3);
The output of console.log() is misleading; While you log that time there was no value. After that it was added. It prints the reference of object , not the state value. So you will see current state value all the time.

Async loading files with Angular into an object array and keeping their order

I'm not loading scripts, I'm reading XML files into an array of objects.
My first [dumb] attempt was something like:
for (var i = 1; i < n; i++) {
var filePath = "xml/chapter_"+i+".xml";
$http.get( filePath ).success(function (data) {
$scope.chaptersData.push (data._chapter);
});
}
I quickly figured out this was no good, because the array will be filled in the order the files finished loading, not when they started (a race condition) and the smaller ones will finish first. I can't use the value of 'i' because of course it gets to 'n' long before any of the loading finishes.
After that I thought success(function (data, i) { would work, but it didn't. I'm out of simple ideas, and before I come up with some kind of Rube Goldberg kludge I thought I would ask the wisdom of the Internets if there is a 'right' way to do this.
You can pass i into a function which performs the request.
for (var i = 1; i < n; i++) {
getFile(i);
}
function getFile(index){
var filePath = "xml/chapter_" + index + ".xml";
$http.get( filePath ).success(function (data) {
$scope.chaptersData[index] = data._chapter;
});
}
Within the getFile function, the value of the parameter index will not change as i changes in the outer function. So it will still be at its initial value when the success function executes and can be used for populating the array.
Just get data as an object like
{
order: [] // just your chapter id or whatever identifier value in desired order
data: // whatever you're getting now
}
In your .success (which is btw depreciated and you should use .then() instead) just do
orderedChapters = [];
for (var i = 0; i < order.length; i++) {
for (var j = 0; j < data.length; i++) {
if (order[i] == data[j].id) {
orderedChapters.push(data[j]);
}
}
}

Node.js function output to variable

i have a grep function that i am using to seperate some data out.
I ran into an issue, instead of outputting the data to the console, i need it to store it to a variable.
for example, here is my actual function.
function funGrep(cmd,callback,search,args){
exec(cmd,function(err,stdout){
if(!stdout)
return;
var lines = stdout.toString().split(EOL);
var re = new RegExp(search,args);
for(var line in lines){
var results = lines[line].match(re);
if(results){
for(var i = 0; i < results.length; i++){
callback(results[i]);
}
}
}
});
}
and here is my actual calling of the function into play.
funGrep("ping -n 3 google.com",console.log,"time=[0-9\.]+ ?ms");
instead of logging the output to the console, how can i just assign it to a variable like output.
thank you!
All you should have to do is create your own callback function that does whatever you need it to do with your data/results. It would look something like this:
function theCallback (data) {
... do whatever you want with your data ...
}
And then instead of console.log, you would pass in this function as an argument.
funGrep("ping -n 3 google.com",theCallback,"time=[0-9\.]+ ?ms");
You could you the callback to append the data to a variable, and modify the callback to notify the handler when the function has finished:
function funGrep(cmd,callback,search,args){
exec(cmd,function(err,stdout){
if(err){
console.log(err);
return;
}
if(!stdout)
return;
var lines = stdout.toString().split(EOL);
var re = new RegExp(search,args);
for(var line in lines){
var results = lines[line].match(re);
if(results){
for(var i = 0; i < results.length; i++){
callback(results[i],false);
}
}
}
callback(null,true); //finsished
});
}
var myData = [];
funGrep("ping -n 3 google.com",function(result,finished){if(!finished) myData.push(result); else goOn();},"time=[0-9\.]+ ms");
function goOn(){
//funGrep finished
console.log("Result: " + myData);
}

Javascript async in nested for loop MongoDB

I have an asynchronous function inside a for loop nested in another for loop.
// recipesArray is an array of arrays of objects
// recipeObject is an array of objects
// currentRecipe is an object
connectToDb(function(){
// LOOP 1
for (var i=0, l=recipesArray.length; i < l; i++) {
// recipeObject is an
var recipeObject = recipesArray[i];
// LOOP 2
for (var x=0, y=recipeObject.length; x < y; x++) {
var currentRecipe = recipeObject[x];
// this is an asynchronous function
checkRecipe(currentRecipe, function (theRecipe) {
if (theRecipe === undefined) {
console.log('RECIPE NOT FOUND');
} else {
console.log('RECIPE FOUND', theRecipe);
}
});
}
}
});
I need to add data to the recipesArray based on the results of the checkRecipe function.
I've been trying different things...
- do i try to keep track of i and x...
- do i try to have multiple callbacks...
- do i even need to do all of that, or is there some other way....
I also tried using the async library for node(which actually has been very helpful with other situations), but the forEach doesn't take objects(only an array).
Stuck.
Any suggestions would be greatly appreciated.
Assuming checkRecipe() can be run in parallel with no limits, here's how you might use async.each():
connectToDb(function() {
async.each(recipesArray, function(subArray, callback) {
async.each(subArray, function(currentRecipe, callback2) {
checkRecipe(currentRecipe, function(theRecipe) {
if (theRecipe === undefined)
return callback2(new Error('Recipe not found'));
callback2();
});
}, callback);
}, function(err) {
if (err)
return console.error('Error: ' + err);
// success, all recipes found
});
});

Simpy cannot iterate over javascript object?

I have scoured the other question/answer for this and implemented everything and I still cannot access the values of the object. Here's the code I am using:
function apply_voucher(voucher) {
var dates = $.parseJSON($("[name='dates']").val());
var voucher_yes_no = new Array();
var voucher_reduction = new Array();
if(voucher.length > 0)
{
$.each(dates, function(room_id, these_dates) {
$.post('/multiroom/check_voucher/'+voucher+'/'+room_id, function(result) {
if(result.result == 'ok') {
voucher_yes_no.push('yes');
voucher_reduction.push(result.voucher_reduction);
} else {
voucher_yes_no.push('no');
}
}, 'json');
});
// check if there are any yes's in the array
if('yes' in voucher_yes_no) {
console.log("no yes's");
} else {
console.log(voucher_reduction);
console.log(typeof voucher_reduction);
for (var prop in voucher_reduction) {
console.log(prop);
console.log(voucher_reduction[prop]);
if (voucher_reduction.hasOwnProperty(prop)) {
console.log("prop: " + prop + " value: " + voucher_reduction[prop]);
}
}
}
}
}
Apologies for the constant console logging - I'm just trying to track everything to make sure it's all doing what it should. The console output I get from this is below:
...which shows the object containing one value, "1.01" and my console.log of the typeof it to make sure it is actually an object (as I thought I was going mad at one point). After this there is nothing from inside the for-in loop. I have tried jquery's $.each() also to no avail. I can't understand why nothing I'm trying is working!
It does not work because the Ajax call is asynchronous!
You are reading the values BEFORE it is populated!
Move the code in and watch it magically start working since it will run after you actually populate the Array!
function apply_voucher(voucher) {
var room_id = "169";
var dates = $.parseJSON($("[name='dates']").val());
var voucher_reduction = new Array();
$.post('/multiroom/check_voucher/'+voucher+'/'+room_id, function(result) {
if(result.result == 'ok') {
voucher_reduction.push(result.voucher_reduction);
}
console.log(voucher_reduction);
console.log(typeof voucher_reduction);
for (var prop in voucher_reduction) {
console.log(prop);
console.log(voucher_reduction[prop]);
if (voucher_reduction.hasOwnProperty(prop)) {
console.log("prop: " + prop + " value: " + voucher_reduction[prop]);
}
}
}, 'json');
}
From what it looks like, you plan on making that Ajax call in a loop. For this you need to wait for all of the requests to be done. You need to use when() and then(). It is answered in another question: https://stackoverflow.com/a/9865124/14104
Just to say for future viewers that changing the way I did this to use proper deferred objects and promises, which blew my head up for a while, but I got there! Thanks for all the help, particularly #epascarello for pointing me in the right direction :) As soon as I started doing it this way the arrays began behaving like arrays again as well, hooray!
Here's the final code:
function apply_voucher(voucher) {
var booking_id = $("[name='booking_id']").val();
var dates = $.parseJSON($("[name='dates']").val());
if(voucher.length > 0) {
var data = []; // the ids coming back from serviceA
var deferredA = blah(data, voucher, dates); // has to add the ids to data
deferredA.done(function() { // if blah successful...
var voucher_yes_no = data[0];
var voucher_reduction = data[1];
if(voucher_yes_no.indexOf("yes") !== -1)
{
console.log("at least one yes!");
// change value of voucher_reduction field
var reduction_total = 0;
for(var i = 0; i < voucher_reduction.length; i++) {
reduction_total += voucher_reduction[i];
}
console.log(reduction_total);
}
else
{
console.log("there are no yes's");
}
});
}
}
function blah(data, voucher, dates) {
var dfd = $.Deferred();
var voucher_yes_no = new Array();
var voucher_reduction = new Array();
var cycles = 0;
var dates_length = 0;
for(var prop in dates) {
++dates_length;
}
$.each(dates, function(room_id, these_dates) {
$.post('/multiroom/check_voucher/'+voucher+'/'+room_id, function(result) {
if(result.result == 'ok') {
voucher_reduction.push(result.voucher_reduction);
voucher_yes_no.push('yes');
} else {
voucher_yes_no.push('no');
}
++cycles;
if(cycles == dates_length) {
data.push(voucher_yes_no);
data.push(voucher_reduction);
dfd.resolve();
}
}, 'json');
});
return dfd.promise();
}
Can you show how voucher_reduction is defined?
I am wondering where the second line of the debug output comes from, the one starting with '0'.
in this line:
console.log(vouncher_reduction[prop]);
^
The name of the variable is wrong (then) and probably that is breaking your code.
I think there are no problem with your loop.
But perhaps with your object.
Are you sure what properties has enumerable ?
Try to execute this to check :
Object.getOwnPropertyDescriptor(voucher_reduction,'0');
If it return undefined, the property was not exist.

Categories