0

I am attempting to populate an array and use it later. I am getting "undefined" result when I try accessing objects at indexes.

$(document).ready(function() {

  var streamers = ["freecodecamp", "GeoffStorbeck", "terakilobyte"];

  var cb = '?client_id=5j0r5b7qb7kro03fvka3o8kbq262wwm&callback=?';
  var url = 'https://api.twitch.tv/kraken/';
  var result = {};


  streamers.forEach(function(stream)
  {
    $.getJSON(url + 'streams/' + stream + cb).success(function(data)
    {
        var streaming = (data.stream === null) ? false : true;
        result.push(stream + " - " + streaming);
    });
  });

  alert(result[0]);
  alert(result[1]);
  alert(result[2]);
});
vaultah
  • 44,105
  • 12
  • 114
  • 143
Asad
  • 21,468
  • 17
  • 69
  • 94
  • JSON functions are usually `asynchronous` which means that your array is being built after the main block of code is finished. – somethinghere Sep 21 '15 at 13:51
  • [`$.getJSON`](http://api.jquery.com/jquery.getjson/) is asynchronous – blgt Sep 21 '15 at 13:51
  • 1
    Related to [How to return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call), but there is probably a better duplicate somewhere for handling asynchronous operations in loops. – apsillers Sep 21 '15 at 13:52
  • See http://stackoverflow.com/q/23667086/218196 for an explanation of the issue. – Felix Kling Sep 21 '15 at 13:54

4 Answers4

3

What you need is a callback, as your getting something for the server mostly happens in asynchronous processes, which mean that the code continues executing and only adds something to the array when something is returned. You want the script to only alert things when it completed a task, which is why we call it a call back, as it will call the passed function back (aka when it's done).

$(document).ready(function() {

  var streamers = ["freecodecamp", "GeoffStorbeck", "terakilobyte"];

  var cb = '?client_id=5j0r5b7qb7kro03fvka3o8kbq262wwm&callback=?';
  var url = 'https://api.twitch.tv/kraken/';
  // This should be initialised as an array, not an object!
  var result = [];
  
  function callback(){
    // I add this line to reduce the amount of alerts
    if(result.length !== 3) return;
    alert(result[0]);
    alert(result[1]);
    alert(result[2]);
  }


  streamers.forEach(function(stream){
    $.getJSON(url + 'streams/' + stream + cb).success(function(data){
        var streaming = (data.stream === null) ? false : true;
        result.push(stream + " - " + streaming);
        callback();
    });
  });
});
somethinghere
  • 16,311
  • 2
  • 28
  • 42
  • Ah yes indeed! My bad. – somethinghere Sep 21 '15 at 13:54
  • Also I would use the success to do the next call – mplungjan Sep 21 '15 at 13:55
  • @mplungjan Yeah you could, but for the purposes of this example it's not necessary. It would be better practise, but since I'm checking for the length it makes no difference in the end result. – somethinghere Sep 21 '15 at 13:56
  • I agree with @somethinghere - When you come to work on the results, it would be better to change the way this works. But for now it answers the question about viewing the data. Although I think my answer is better :P – Adrian Lynch Sep 21 '15 at 14:01
  • 1
    @AdrianLynch I like your answer and upvoted it as well - its better at explaining asynchronous imho. – somethinghere Sep 21 '15 at 14:05
1

$.getJSON is asynchronous.

Your alerts are running before you have data in the array.

To view the data when it's available, move the alerts (or better yet, console.logs) up into the success callback:

$(document).ready(function() {

    var streamers = ["freecodecamp", "GeoffStorbeck", "terakilobyte"];
    var cb = '?client_id=5j0r5b7qb7kro03fvka3o8kbq262wwm&callback=?';
    var url = 'https://api.twitch.tv/kraken/';
    var result = {};

    streamers.forEach(function(stream) {
        $.getJSON(url + 'streams/' + stream + cb).success(function(data) {
            var streaming = (data.stream === null) ? false : true;
            result.push(stream + " - " + streaming);
            console.log(result);
        });
    });
});

Moving from looking at the results to using the results, you'll want to break things up a bit:

$(document).ready(function() {

    var streamers = ["freecodecamp", "GeoffStorbeck", "terakilobyte"];
    var cb = '?client_id=5j0r5b7qb7kro03fvka3o8kbq262wwm&callback=?';
    var url = 'https://api.twitch.tv/kraken/';
    var result = {};

    var getStreams = streams.map(function (stream) {
        return $.getJSON(url + 'streams/' + stream + cb).success(function(data) {
            var streaming = (data.stream === null) ? false : true;
            result.push(stream + " - " + streaming);
            console.log(result);
        });
    });

    var onResultsSuccess = function (results) {
        console.log("I have all my streams, let's take a look at them:", results);
    };

    var onResultsFail = function () {
        console.log("Something's up!", arguments);
    };

    $.when(getStreams).then(onResultsSuccess, onResultsFail);

});

Untested so assume it's pseudo!

Adrian Lynch
  • 8,237
  • 2
  • 32
  • 40
1

You are accessing result before the $.getJSON callbacks have been executed. Many questions like this have been answered already, see Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference for an explanation of the problem.

You should make use of the fact that all Ajax methods return promises/deferred objects and use $.when to wait for all of them to complete:

var deferreds = streamers.map(function(stream) {
  return $.getJSON(url + 'streams/' + stream + cb).then(function(data) {
    var streaming = (data.stream === null) ? false : true;
    return stream + " - " + streaming;
  });
});

$.when.apply(null, deferreds).then(function(result) {
  console.log(result);
});
Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
0

Not only should you defer the alert to after the calls, you should not loop Ajax externally..

Here is what I would suggest

function getStream() {
   if (result.length >= streamers.length) return;
   var stream = streamers[result.length];
   $.getJSON(url + 'streams/' + stream + cb).success(function(data){
     var streaming = (data.stream === null) ? false : true;
     result.push(stream + " - " + streaming);
     getStream();
   });
}
var streamers = ["freecodecamp", "GeoffStorbeck", "terakilobyte"];
var cb = '?client_id=5j0r5b7qb7kro03fvka3o8kbq262wwm&callback=?';
var url = 'https://api.twitch.tv/kraken/';
// This should be initialised as an array, not an object!
var result = [];
$(function() {
  getStream();      
});
mplungjan
  • 169,008
  • 28
  • 173
  • 236