-1

I have created a web page that interacts with the Twitch API. The app basically shows a twitch user's name, logo and whether they are offline or online.

To do this. I did two AJAX calls:

  var twitches = [
    "freecodecamp",
    "orb",
    "medrybw",
    "storbeck",
    "terakilobyte",
    "habathcx",
    "RobotCaleb",
    "thomasballinger",
    "noobs2ninjas",
    "beohoff"
  ];

  // Retrieve the username and logo of the user
for(x=0;x < twitches.length; x++){
      $.ajax({
        url:"https://api.twitch.tv/kraken/users/"+ twitches[i] + "?client_id=fmg7qm7vtgxozyr7dj2icwrumdb186",
        type: "GET",
        dataType: "jsonp",
        success: function(x) {
          if (x.logo == null) {
            x.logo =
              "http://www.dotcomsolicitors.com/wp-content/uploads/sites/491/2017/08/user-icon-300x300.png";
          }
          $(".tbody").append(
            "<tr><td>" +
              '<a href="https://go.twitch.tv/' +
              x.name +
              '">' +
              '<img src="' +
              x.logo +
              '"/></a></td><td><p>' +
              '<a href="https://go.twitch.tv/' +
              x.name +
              '">' +
              x.name +
              '</a></p></td><td class="onOroff"></td></tr>'
          );
        }
      });
}

and this AJAX call checks whether the user is online or not. If x.stream === null this means the user is offline, so a no sign will be appended to the last column. But there seems to a problem with my loop and I don't know how to fix it. The codepen is https://codepen.io/mrsalami/pen/yPaOqO.

for(x=0;x < twitches.length; x++){
$.ajax({
    url:"https://api.twitch.tv/kraken/streams/"+ twitches[i] + "?client_id=fmg7qm7vtgxozyr7dj2icwrumdb186",
    type: "GET",
    dataType: "jsonp",
    success: function(x) {
      console.log(x.stream);

      if (x.stream === null) {
        $(".onOroff").append(
          '<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/No_sign.svg/300px-No_sign.svg.png" alt="Image result for no symbol png"/>');
      } else {
        $(".onOroff").append(
          '<img src="https://www.shareicon.net/download/2015/10/24/136117_ok_300x300.png" alt="Image result for yes symbol png"/>'
        );
      }
    }
  });

}

Console Log:

null
null

Object {
  _id: 26678888912,
  _links: Object {
    self: "https://api.twitch.tv/kraken/streams/medrybw"
  },
  average_fps: 24.025974026,
  channel: Object {
    _id: 50332395,
    _links: Object {},
    background: null,
    banner: null,
    broadcaster_language: "en",
    created_at: "2013-10-18T22:13:12Z",
    delay: null,
    display_name: "MedryBW",
    followers: 23013,
    game: "StarCraft",
    language: "en",
    logo: "https://static-cdn.jtvnw.net/jtv_user_pictures/medrybw-profile_image-19fce7e1b0d6c194-300x300.jpeg",
    mature: false,
    name: "medrybw",
    partner: false,
    profile_banner: null,
    profile_banner_background_color: "",
    status: "24/7 Classic Starcraft VoD stream 2000-2012 (6344 VoDs)",
    updated_at: "2017-11-08T08:33:30Z",
    url: "https://www.twitch.tv/medrybw",
    video_banner: null,
    views: 854932
  },
  created_at: "2017-11-08T04:26:27Z",
  delay: 0,
  game: "StarCraft",
  is_playlist: false,
  preview: Object {
    large: "https://static-cdn.jtvnw.net/previews-ttv/live_user_medrybw-640x360.jpg",
    medium: "https://static-cdn.jtvnw.net/previews-ttv/live_user_medrybw-320x180.jpg",
    small: "https://static-cdn.jtvnw.net/previews-ttv/live_user_medrybw-80x45.jpg",
    template: "https://static-cdn.jtvnw.net/previews-ttv/live_user_medrybw-{width}x{height}.jpg"
  },
  stream_type: "live",
  video_height: 768,
  viewers: 22
}

​
Object {
  _id: 26669328176,
  _links: Object {
    self: "https://api.twitch.tv/kraken/streams/orb"
  },
  average_fps: 60,
  channel: Object {
    _id: 20519306,
    _links: Object {},
    background: null,
    banner: null,
    broadcaster_language: "en",
    created_at: "2011-02-17T07:14:53Z",
    delay: null,
    display_name: "Orb",
    followers: 166319,
    game: "Call of Duty: WWII",
    language: "en",
    logo: "https://static-cdn.jtvnw.net/jtv_user_pictures/orb-profile_image-076d545e806d2190-300x300.png",
    mature: false,
    name: "orb",
    partner: true,
    profile_banner: "https://static-cdn.jtvnw.net/jtv_user_pictures/0e56159f562dcf9f-profile_banner-480.png",
    profile_banner_background_color: "",
    status: "24/7 Race to Prestige  Annual Call of Duty Marathon | OFFICIAL !Merch  | !DXRacer Giveaway | Powered by: !Elgato !Razer !Nvidia",
    updated_at: "2017-11-08T08:33:25Z",
    url: "https://www.twitch.tv/orb",
    video_banner: "https://static-cdn.jtvnw.net/jtv_user_pictures/orb-channel_offline_image-0d0763efc52d70f3-1920x1080.png",
    views: 16628568
  },
  created_at: "2017-11-06T22:45:13Z",
  delay: 0,
  game: "Call of Duty: WWII",
  is_playlist: false,
  preview: Object {
    large: "https://static-cdn.jtvnw.net/previews-ttv/live_user_orb-640x360.jpg",
    medium: "https://static-cdn.jtvnw.net/previews-ttv/live_user_orb-320x180.jpg",
    small: "https://static-cdn.jtvnw.net/previews-ttv/live_user_orb-80x45.jpg",
    template: "https://static-cdn.jtvnw.net/previews-ttv/live_user_orb-{width}x{height}.jpg"
  },
  stream_type: "live",
  video_height: 864,
  viewers: 624
}
null
null
null
null
null
null
James Ocean
  • 145
  • 1
  • 2
  • 14
  • use Promise.all – Álvaro Touzón Nov 08 '17 at 08:34
  • @ÁlvaroTouzón where? lool – James Ocean Nov 08 '17 at 08:37
  • Can't see a `for` loop in the question. (might be in the codepen, but that's a 3rd party site and not part of the question). – freedomn-m Nov 08 '17 at 08:37
  • Given the question content, it's *possible* the answer will be here: https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example?rq=1 (I've not marked as duplicate as the question is incomplete). – freedomn-m Nov 08 '17 at 08:38
  • Basically, if you have `for (var x ..` then `x` will be the last value of the for loop *before* any of the callbacks are made. Add some `console.logs` to see when things are called. – freedomn-m Nov 08 '17 at 08:40
  • @freedomn-m ok right I have added some console logs – James Ocean Nov 08 '17 at 08:47
  • I didn't mean to the question :) just in your code so you can see the order things are happening. – freedomn-m Nov 08 '17 at 09:26
  • Interested as to why you might have removed the accepted answer status on this? Was there something more you needed from an answer? – Jamiec Nov 09 '17 at 17:26
  • @Jamiec because I have tried your solution and it does not work. – James Ocean Nov 11 '17 at 04:26
  • Well id be happy to try to help, but "does not work" doesn't give me much to go on. Do you get an error? Does something unexpected happen? Do you see both requests go out in your dev tools? – Jamiec Nov 11 '17 at 12:27

1 Answers1

3

Basically your code is one big race condition. In one ajax callback you create a table cell and in the other you try to append something to an empty cell. If the first returns before the second then all should work. If not, there is no empty cell yet for the second to append to.

Reorganise your code to use Promises, and use Promise.all to wait for both to return. Once that happens you can create your content all in one place.

Something like this.

var twitches = [...];// your array
for(var i=0;i<twitches.length;i++){
    Promise.all([
        $.ajax(...),  // first ajax call, without the success callback
        $.ajax(...)   // second ajax call, without the success callback
    ]).then(function(results){
       var usernameAndLogoResult = results[0];
       var statusResult = results[1];
       // build your html here
    });
}
Jamiec
  • 133,658
  • 13
  • 134
  • 193