This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
Here is my code
for (var i=0; i<5; i++) {
var url = generate_url(i) ;
$http.get(url).then(function(response){
var param2 = response.data.param2
$scope.outputData.push({'index':i, 'param':param2}) ;
}) ;
}
In this example, I suppose to obtain an array in the $scope.outputData with data similar to this:
[
{'index':0,param:'a'},
{'index':1,param:'b'},
{'index':2,param:'c'},
{'index':3,param:'d'},
{'index':4,param:'e'},
]
but what i get is data like this:
[
{'index':4,param:'a'},
{'index':4,param:'b'},
{'index':4,param:'c'},
{'index':4,param:'d'},
{'index':4,param:'e'},
]
In this case, the externel data that I mean is the variable i,
Please could you tell me the trouble ? and how do I proceed to attend my goal? Thank you in advance and sorry for my english :)
You can create a closure over the i variable to make sure it still has the value you want it to have when you use it.
for (var i=0; i<5; i++) {
(function(counter) {
var url = generate_url(i);
$http.get(url).then(function(response){
var param2 = response.data.param2
$scope.outputData.push({'index':counter, 'param':param2}) ;
});
}(i));
}
But if the ordering of the resulting array matters, you will have to create a temporary array and then sort it on index.
You can use $q.all if you don't want to process any of the requests untill they have alle completed.
Something like this:
var promises = {};
for (var i=0; i<5; i++) {
var url = generate_url(i) ;
promises[i] = $http.get(url);
}
$q.all(promises).then(function(result) {
for (index in result) {
var param2 = result[index].data.param2
$scope.outputData.push({'index':index, 'param':param2}) ;
}
});
That should also preserve the ordering.
The docs for $q are here.
This is a closure issue, the correct way to do it would be
for (var i=0; i<5; i++) {
getData(i);
}
var getData=function(index) {
var url = generate_url(index) ;
$http.get(url).then(function(response){
var param2 = response.data.param2
$scope.outputData.push({'index':index, 'param':param2}) ;
}) ;
}
Related
newbie at JavaScript and Postman here.
I have set up a basic test in postman using JS to compare names in a web response to names in a data file. The array of names is in an external data csv file.
I want to loop through the array, but I get an error:
"ReferenceError | i is not defined"
Code:
var newResponse = responseBody;
let nameArray = data.name;
for (let i = 0; i < nameArray.length; i++) {
console.log(nameArray.length);
}
pm.test("Web vs. Data: Person", function() {
pm.expect(newResponse.Item[i].name).to.equal(nameArray.Item[i].person);
});
console.log(newResponse.Item[i].name);
console.log(nameArray.Item[i].person);
Your end scope "}" character missing please change with this code;
var newResponse = responseBody;
let nameArray = data.name;
for (let i = 0; i < nameArray.length; i++) {
console.log(nameArray.length);
pm.test("Web vs. Data: Person", function () {
pm.expect(newResponse.Item[i].name).to.equal(nameArray.Item[i].person);
});
console.log(newResponse.Item[i].name);
console.log(nameArray.Item[i].person);
}
let is block scoped so it will cause ReferenceError out of the for loop. The variable i will not be referred outside of the for loop. So you've to move your codeblock inside the for loop like below. Hope this helps :)
var newResponse = responseBody;
let nameArray = data.name;
for(let i = 0; i < nameArray.length; i++){
console.log(nameArray.length);
pm.test("Web vs. Data: Person" ,function(){
pm.expect(newResponse.Item[i].name).to.equal(nameArray.Item[i].person);
});
console.log (newResponse.Item[i].name);
console.log(nameArray.Item[i].person);
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I am doing simple app in javascript. I have "main_script" where I invoke everything. There is global variable "feeds" which is an array, like this:
var feeds = [];
Then after that I use function, that loads JSON file from multipe URLs (also array):
feeds = LoadJsonFeeds(urls); // Load feeds
console.log("main_code feeds.length: " + feeds.length);
That console log I mention later. Ok and now he is my LoadJsonFeeds (in different .js file, just a function):
function LoadJsonFeeds(urls) {
var feeds_tmp = [];
// URLs can be more - for example 50 feeds from url[0] and 20 from url[1]
for(var u = 0; u < url.length; u++) {
$.getJSON(url[u], function(data) {
var allFeeds = data.Result.Items; // allFeeds without check if they are ok
for(var i = 0; i < allFeeds.length; i++) {
// Is feed ok?
if (allFeeds[i].Text != null)
{
// Some more checking, but lets say ok for this
feeds_tmp.push(allFeeds[i]);
}
// This I mention later
console.log("LoadJson feeds.length: " + feeds.length);
}
});
}
console.log("LoadJson return"); // Mention later
return feeds_tmp;
}
And here is the problem I am struggling with. When I look at the console, here what I see:
LoadJson return
main_code feeds.length: 0
LoadJson feeds.length: 1
LoadJson feeds.length: 2
LoadJson feeds.length: 3
etc...
I just don't see the logic behind it! How can it first returned the function with nothing, then the main_script continues. After that, the function ALTER one by one the global variable "feeds". I suspect the anonymous function, but don't know what to do with it.
What am I trying to achive? Simple, I wanted to have function, that load JSON files from URLs. For example url[0] has 50 feeds, url[1] has 20. If everything is ok then it should return array of 70 feeds. I use this for the first time in main_script, and then in interval for update, which I call every few seconds. In this function I check, which feed is new and put it somewhere else:
function UpdateFeeds(url) {
console.log("updating...");
var feeds_tmp = LoadJsonFeeds(url);
console.log("Update feeds_tmp.length: " + feeds_tmp.length); // This is 0
for(var f_tmp = 0; f_tmp < feeds_tmp.length; f_tmp++) { // This does not happen because feeds_tmp.length = 0
for(var f = 0; f < feeds.length; f++) {
// Check what feed is new and put it somewhere else (the new one)
}
}
}
feeds = feeds_tmp; // Make all new feeds the global variable
}
But since the returned array is 0, that forloop does not happen. But it will still alter the global variable "feeds" anyway. For the main function it does not matter. In global variable the datas are in it, but I really need to find a new ones and do some work with it. But since it does not work that way, I am pretty lost.
What am I missing and how to fix this? Thank you!
Your console.log("LoadJson feeds.length: " + feeds.length); called later because its a asynchronous call , you can update this function as
function LoadJsonFeeds(urls,callback) {
var feeds_tmp = [];
// URLs can be more - for example 50 feeds from url[0] and 20 from url[1]
for(var u = 0; u < url.length; u++) {
$.getJSON(url[u], function(data) {
var allFeeds = data.Result.Items; // allFeeds without check if they are ok
for(var i = 0; i < allFeeds.length; i++) {
// Is feed ok?
if (allFeeds[i].Text != null)
{
// Some more checking, but lets say ok for this
feeds_tmp.push(allFeeds[i]);
}
// This I mention later
console.log("LoadJson feeds.length: " + feeds.length);
}
if(u==url.length.1) // to make sure all URL loaded
callback(feeds_tmp)
});
}
}
And call your function as
feeds = LoadJsonFeeds(urls,function(feeds){
console.log("main_code feeds.length: " + feeds.length);
}); // Load feeds
I'm new to AngularJS, and is experimenting AngularJS with Twitch API.
I have a list of channels that I'm interested in, defined as var channels.
Then I use the $http.get function to loop through another array, twitchList.channels, which contains the API addresses that I'm supposed to call.
(function() {
var app = angular.module('twitchList', []);
app.controller('twitchController', ['$http', function($http){
var twitchList = this;
twitchList.channels = [];
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff", "MedryBW"];
for (var i = 0; i < channels.length; i++ ) {
twitchList.channels.push({
name: channels[i],
api: 'https://api.twitch.tv/kraken/streams/' + channels[i],
})
}
var data_list = [];
for (var j = 0; j < twitchList.channels.length; j++) {
$http.get(twitchList.channels[j].api).success(function(data){
data_list.push(data);
})
}
// Issue arises here!
console.log(data_list);
console.log(data_list.length);
}]);
})();
The API calls seems to be working perfectly, however, I need to get the results of the API call into an array, called data_list. Now, when I print data_list, and data_list.length, what happens is that data_list.length always returns 0, and data_list is sometimes populated (meaning it's either 0 size array or 9 size array). Even though the property of the array has a length 9, but calling .length always gives 0.
This let me think that the controller code is not executed line by line? Or is there something wrong with my logic?
Can someone give me a pointer? Thanks
No, this line:
data_list.push(data);
will be executed when you receive a response on the http request sent a line above. Hence the following lines:
console.log(data_list);
console.log(data_list.length);
will output [] and 0
I've not used it before, but could you possibly use $q.all in order to resolve multiple promises? I've used the equivalent $.when function in jQuery to achieve this in the past.
var data_list = [];
var promise_array = [];
var request;
for (var j = 0; j < twitchList.channels.length; j++) {
request = $http.get(twitchList.channels[j].api);
request.success(function(data) {
data_list.push(data);
});
promise_array.push(request);
}
$q.all(promise_array).then( function() {
console.log(data_list);
console.log(data_list.length);
});
I have this code:
rtclient.listFiles = function(callback) {
gapi.client.load('drive', 'v2', function() {
gapi.client.drive.files.list({
'maxResults': 20
}).execute(callback);
});
}
which I try to assign to an array using this code:
var arrayStore;
rtclient.listFiles(function(x) {
for(var i in x.items) {
arrayStore.push(x.items[i].id);
}})
However, the array arrayStore is not storing anything. This is using the Google Drive API and I am trying to access some file IDs to iterate over. Can anyone help?
the point is you should have a error like:
TypeError: Cannot call method 'push' of undefined
because you haven't defined it as an array, the other point is don't use for(...in) for arrays, you can find reasons about that in this post:
Why is using “for…in” with array iteration such a bad idea?
var arrayStore = [];
rtclient.listFiles(function(x) {
for(var i = 0; i < x.items.length; i++) {
arrayStore.push(x.items[i].id);
}
});
The other important point as #toothbrush has commented is, since the Google Drive API is asynchronous, you can't access the contents of arrayStore immediately, in other word to continue your scenario you should do something like this:
var arrayStore = [];
rtclient.listFiles(function(x) {
for(var i = 0; i < x.items.length; i++) {
arrayStore.push(x.items[i].id);
}
nextStep();
});
function nextStep(){
//here you would have access to arrayStore
console.log(arrayStore);
}
Using javascript I get list of facebook friends though it only returns name and id now, but I need to get the picture of each user. I try to loop through the response and then try to call the api to get picture, but due to it's async call I can't associate the returned picture with the index of the friend in the array. *this is kinda a problem that I've had with asynchronous programming in general, is there a standard pattern for this?
Example.
FB.api('me/friends', function(response) {
if(response.error == null){
var friendsSale = response.data;
var len = friendsSale.length;
for(var x=0; x<len; x++){
FB.api(friendsSale[x].id+'/picture', function(response) {
//x no longer is the same x as the initial call, and I can't pass in the orignal array object into the FB.api function to return as part of the response... or can I?
friendsSale[x].pictureUrl = response;
});
}
}
//Then how do I know when I have all the pictures set so I can then set datamodle with the complete friend array?
m.friends(friendsSale);
}
});
Yes, there is a pattern for this: a Closure
...
var len = friendsSale.length;
for (var i = 0; i < len; i++) {
(function() {
var j = i;
FB.api(friendsSale[i].id+'/picture', function(response) {
friendsSale[j].pictureUrl = response;
});
})();
}
To know when all all calls have returned you can simply keep a counter of returned calls, e.g.
...
var len = friendsSale.length;
var returnedCallsCounter = 0;
for (var i = 0; i < len; i++) {
(function() {
var j = i;
FB.api(friendsSale[i].id+'/picture', function(response) {
friendsSale[j].pictureUrl = response;
// Track number of returned calls
returnedCallsCounter++;
// Check if all calls have returned
if (returnedCallsCounter == len) {
m.friends(friendsSale);
}
});
})();
}
Simple solution for you :
All you have to do is query this :
https://graph.facebook.com/user_id/picture
and you will get the users profile picture. For example :
Querying https://graph.facebook.com/4/picture (with no access token BTW - try it in chrome pron incognito mode) :
<img src="https://graph.facebook.com/4/picture">
will yeild this smiling face :
Now you know Marks fbid :P