1

I'm currently reading How do I return the response from an asynchronous call? and it seems to be related to why my code below is not working as expected. My console.log after the getJSON for loop is executing before the loop and therefore is returning null. The console.log within the getJSON loop is returning the objects pushed into the array.

After reading the referenced answers several times I'm more confused on how to implement the solution in a callback or promise. What do I need to do in my code below to either use a callback or promise so that the outer console.log returns data?

function channel() {
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var channelData = [];

    for(var x = 0; x < channels.length; x++) {
        $.getJSON(`https://api.twitch.tv/kraken/channels/${channels[x]}?callback=?`, function(data) {
          console.log(data.display_name);
        //   console.log(data);
          channelData.push( { display_name:data.display_name, logo:data.logo, url:data.url, game:data.game, status:data.status } );
          console.log(channelData);
        });
    }
    console.log(channelData);
}
Community
  • 1
  • 1
j7an
  • 104
  • 2
  • 11

4 Answers4

3

The $.getJSON method returns a Promise, so you could just wait on all of them to complete. Push all of your $.getJSON calls into an array, and pass the array to $.when:

function channel() {
  var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
  var channelData = [];
  var gets = [];
  var success = function(data) {
    channelData.push({
      display_name: data.display_name,
      logo: data.logo,
      url: data.url,
      game: data.game,
      status: data.status
    })
  };

  for (var x = 0; x < channels.length; x++) {
    gets.push(
      $.getJSON(`https://api.twitch.tv/kraken/channels/${channels[x]}?callback=?`)
    );
  }
  $.when.apply(null, gets).done(function() {
    var results = arguments;
    for (var i = 0, z = results.length; i < z; i++) {
      var data = results[i][0];
      success(data);
    }
    console.log(channelData);
  });
}

channel()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

The trick here is that what gets returned by $.getJSON is Promise-like (it's actually a jQuery Deferred object. Also, the success callback will not be called before the $.when resolves.

So, you need to parse the argument to the function passed to then $.when's done function.

Unfortunately, this doesn't work like a typical call to Promise.all. Rather, it's actually an object, with numeric keys. The values of those keys are arrays of the arguments which would normally be passed to a success callback: namely, the data, the text of the result ("success"), and the jqXHR object.

So, we go through each of those properties and get the first value, then pass that to the function which parses the data. Phew :).

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • I copied your code, commented out all the console.log except for the last one within .when. It's still returning a null in Chrome. – j7an Jun 22 '16 at 01:39
  • Yeah, that's even more different than `null` or `undefined`... Looks like the success functions of each `getJSON` call are not run before the `when` is resolved... Unfortunately, work calls, so I won't be able to look into this much. I'd take a look at what arguments are passed to the function in the `then`... it should be an array of all results of the ajax calls.. – Heretic Monkey Jun 22 '16 at 01:46
1

$.getJSON is an asychronous function so the code is not going to wait its response before moving on. That is, the code will run through the for loop issuing multiple async call then invoke the outer console.log immediately.

getJSON() API documentation

To correctly do the outer "console.log", you'll have to add a callback function to your channel function and it will be called whenever there is a response back from the async call.

Example:

function channel(callback) {
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var channelData = [];

    for(var x = 0; x < channels.length; x++) {
        $.getJSON(`https://api.twitch.tv/kraken/channels/${channels[x]}?callback=?`, function(data) {
          console.log(data.display_name);;
          channelData.push( { display_name:data.display_name, logo:data.logo, url:data.url, game:data.game, status:data.status } );
          return callback(channelData);
        });
    }
}

channel(function(response) {
    console.log(response); // Where response is your channel Data
    /* do something with the return channel data */
    // Note this function will be called multiple times
})
Samuel Toh
  • 18,006
  • 3
  • 24
  • 39
  • Why do you have `return callback(channelData);`? I'm getting a `twitch-tv.js:79 Uncaught TypeError: callback is not a function` – j7an Jun 22 '16 at 01:23
  • The idea is to pass a function() as a parameter for channel(). Then return xxx just means parse the function you passed into channel() and return the value, which is typically just a true. How are you calling channel? – Samuel Toh Jun 22 '16 at 01:30
  • try. $(document).ready(function() { channel(function(data) { console.log('hello!!! -> ' + data); } ); }); – Samuel Toh Jun 22 '16 at 01:38
1
function getData(channel){
    return new Promise(function(resolve,reject){
        try{
            $.getJSON(`https://api.twitch.tv/kraken/channels/${channel}?callback=?`, function(data) {
                resolve(data);
            })
        }catch(ex){
            reject(ex);
        }
    })
}
function channel() {
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var promises=[];
    for(var x = 0; x < channels.length; x++) {
        promises.push(getData(channels[x]));
    }
    Promise.all(promises).then(datas){
        var channelData = [];
        for(var data in datas){
            channelData.push( { display_name:data.display_name, logo:data.logo, url:data.url, game:data.game, status:data.status } );
        }
        console.log(channelData);
    }
}
gu mingfeng
  • 1,010
  • 8
  • 10
1

By passing an array of deferreds to $.when:

var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

$.when.apply($, channels.map(x => $.getJSON(`https://api.twitch.tv/kraken/channels/${x}?callback=?`)))
  .then(function(){
    // here you'll have all the responses
    console.log(arguments)
  })
Community
  • 1
  • 1
moonwave99
  • 21,957
  • 3
  • 43
  • 64