0

I am coding an app using twitch.tv API, for which i need to make multiple getJSON() calls for different users. Is there any explanation for the following output.

//users array contains the list of users for which data is fetched
var users = ["freecodecamp", "brunofin", "storbeck", "medrybw", "comster404", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];

When using the simple for loop:

for (var i = 0; i < users.length; i++) {
            var user = users[i];
            $.getJSON("https://api.twitch.tv/kraken/streams/" + user + "?callback=?", function(json) {
                var add = user;
                if(json.status === 422) {
                    add = add + ' ' + "Closed";
                } else {
                    if (json.stream === null) {
                        add = add + ' ' + "Offline";
                    } else {
                        add = add + ' ' + json.stream.game;
                    }
                }
                $("#userList").append("<div>" + add + "</div>");
            });
};

Output:

beohoff Closed
beohoff Offline
beohoff Offline
beohoff Offline
beohoff StarCraft: Brood War
beohoff Offline
beohoff Offline
beohoff Offline
beohoff Offline 
beohoff Closed
beohoff Offline

When using forEach:

users.forEach(function(user) {
            $.getJSON("https://api.twitch.tv/kraken/streams/" + user + "?callback=?", function(json) {
                var add = user;
                if(json.status === 422) {
                    add = add + ' ' + "Closed";
                } else {
                    if (json.stream === null) {
                        add = add + ' ' + "Offline";
                    } else {
                        add = add + ' ' + json.stream.game;
                    }
                }
                $("#userList").append("<div>" + add + "</div>");
            });
});

Output:

brunofin Closed
comster404 Closed
storbeck Offline
terakilobyte Offline
freecodecamp Offline
medrybw StarCraft: Brood War
thomasballinger Offline
RobotCaleb Offline
noobs2ninjas Offline
habathcx Offline
beohoff Offline

Are the ajax calls made sequentially in case of forEach or is there something else ?

  • (A)synchronous JAX... you're going to get your responses from the getjson call in whatever randomish order the network gods allow the server responses to reach your client. It's basically a race condition, with NO way to tell what order they'll arrive in. you may be issuing them sequentially, but the responses are going to be essentially randomly ordered. – Marc B Dec 10 '15 at 16:21
  • @MarcB yes i learned about it while searching. But, why are all the user names preserved in case of forEach but not in case of for ? – Prakhar Agarwal Dec 10 '15 at 16:25
  • Related: http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – Kevin B Dec 10 '15 at 16:32

2 Answers2

2

With for (var i = 0; i < users.length; i++)) { ... }, you subsequently assign values 0 till users.length-1 to i and do something with it, but you stay in the same closure.

Due to the asynchronous nature of js, you send all your json requests before receiving the first answer. When your answers arrive, user has value users[users.length-1] and this is then used in all your output.

With users.forEach(function(user) { ... }, you create a new closure for each element of users. In each closure, you have a local user variable which you will use when the response of your json request arives.

Dirk Horsten
  • 3,753
  • 4
  • 20
  • 37
1

when you iterate with for loop and running async code in it's scope, when the first return of the Ajax, the iteration is already over.

with forEach loop you run a callback for each iteration creating a new socpe, and even the iteration are over, each function is now at the event loop waiting to be invoked.

you can do something like this so it will be the same:

for (var i = 0; i < users.length; i++) {
    getUser(user[i]);
};

function getUser(user){
  $.getJSON("https://api.twitch.tv/kraken/streams/" + user + "?callback=?", function(json) {
      var add = user;
      if(json.status === 422) {
          add = add + ' ' + "Closed";
      } else {
          if (json.stream === null) {
              add = add + ' ' + "Offline";
          } else {
              add = add + ' ' + json.stream.game;
          }
      }
      $("#userList").append("<div>" + add + "</div>");
  });
}

best way to understand it is by looping with setTimeout and print i:

for(var i = 0; i < 10; i++){
    setTimeout(function(){ console.log(i) }, 100);
};

which will output: 10 ten times, and run it a function:

for(var i = 0; i < 10; i++){
    log(i)
};

function log(i){ setTimeout(function(){ console.log(i)}, 100) };

hope it was helpful.

Alon Valadji
  • 628
  • 4
  • 8