1

I have got an array of channel names(user_arr) and I need to fetch their details from twitch.tv

Here is the code please read the details below

$(document).ready(function(){
var user_arr = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]


  var newarr =[];
  var streams  = [];
  var channels = [];

for(var i = 0 ; i < user_arr.length; i++){ 
    name = user_arr[i];


var url1 ="https://wind-bow.gomix.me/twitch-api/streams/"+name+"?callback=?"
var url2 ="https://wind-bow.gomix.me/twitch-api/channels/"+name+"?callback=?"


$.ajax({

  type:"GET",
  url:url1,
  dataType:"json",
  async:false,
  success:function(streamed_data){
    streams.push(streamed_data);
  }


});


     $.ajax({

  type:"GET",
  url:url2,
  dataType:"json",
  async:'true',
  success:function(channel_data){
    channels.push(channel_data);
  }


});



     }

console.log(channels);

console.log(channels[1]);

});

As you can see from the code I am trying fetch the details in json but apparently when I push the details into a new array they are not being pushed in the same manner as the user_arr. I believe they are being pushed in order of whichever is received first

I am using jquery and have asked many people regarding this issue but most of the replies were regarding promises (a concept I don't understand and don't know how to use it)

Plus when I console.log(channels); it is showing me the array filled with data but when I console.log(channels.length) right after the previous command the length is 0 and no data is being showned

Can I know what is the best way to get the data in the order of the original array using $.ajax as well as $.getJSON

I tried to apply the map function to the final array to rearrange the array but failed

It would be really helpful if someone could show me how we can apply the map function to the array in order to get the final array based on the user_arr array

  • https://stackoverflow.com/questions/133310/how-can-i-get-jquery-to-perform-a-synchronous-rather-than-asynchronous-ajax-re – jupeter May 25 '18 at 10:15
  • You are using as `async:true` for `channels` so your code `console.log(channels.length)` runs before it is able to fetch the data from the server that's why it is giving an output of zero and as for the `promises` take a look at this [link](https://stackoverflow.com/a/27638517/2417602). – vikscool May 25 '18 at 10:16
  • Do you want channels data in  ["RERUN: Wardi vs. RotterdaM (TvP) - EsportsEarnings Casters Invitational - Group B QM LB", "Imre vs Yogo - Rediffusion", null, "Officer Amy Caprio EOW 05-21-2018", "Some GoLang Today #go #golang #youtube", "Doing iOS development in #swift and hanging out!", "Massively Effective", "Code wrangling"] this order? – Bhumi Shah May 25 '18 at 10:20
  • I want the data in order of the user_arr which had the channel name as array – Rajat Audichya May 25 '18 at 12:56

3 Answers3

3

While it's possible that async: false may solve the issue, it's not a workable solution. It's terrible practice as it blocks the UI thread from updating, making the browser look as though it has hung whilst the request is in progress.

A far better solution would be to work with the asynchronous pattern properly and to aggregate all the requests in to an array of promises. Once these promises have been resolved (ie. when all requests have completed) then you can sort the channel and streams arrays to match the order of the display_names contained within the user_arr. Try this:

var user_arr = ["ESL_SC2", "OgamingSC2", "cretetion", "FreeCodeCamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]

var newarr = [];
var streams = [];
var channels = [];

var promises = user_arr.map(function(username) {
  var deferred = $.Deferred();
  var url1 = `https://wind-bow.gomix.me/twitch-api/streams/${username}?callback=?`;
  var url2 = `https://wind-bow.gomix.me/twitch-api/channels/${username}?callback=?`;

  $.when($.ajax({
    type: "GET",
    url: url1,
    dataType: "json",
    success: function(streamed_data) {
      streams.push(streamed_data);
    }
  }), $.ajax({
    type: "GET",
    url: url2,
    dataType: "json",
    success: function(channel_data) {
      channels.push(channel_data);
    }
  })).done(function() {
    deferred.resolve();
  });

  return deferred.promise();
});

$.when.apply($, promises).done(function() {  
  channels.sort(function(a, b) {
    return user_arr.indexOf(a.display_name) > user_arr.indexOf(b.display_name);
  });
  
  streams.sort(function(a, b) {
    return user_arr.indexOf(a._links.channel.display_name) > user_arr.indexOf(b._links.channel.display_name);
  })
  
  console.log(channels);
  console.log(streams);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Thank you Rory, This solution seems to work and also looks like the optimal solution for this problem, However I have no idea about some functions you have used here, What topics do you suggest are lacking in my example code? should I only need to know about promises and how they work? – Rajat Audichya May 25 '18 at 12:48
  • Yes, that's about the only thing you need to know about to understand how this code works. This should give you a good understanding: https://api.jquery.com/deferred.promise/ – Rory McCrossan May 25 '18 at 13:17
0

This should do the trick, though I really suggest you learn promises if you plan on doing more with this that just a little toy.

var user_arr = ["ESL_SC2", "OgamingSC2", "cretetion", "FreeCodeCamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]

var streams = [];
var channels = [];

var count = 0;

function done() {
  count++;
  if (count === user_arr.length * 2) {
    console.log(streams);
    console.log(channels);
  }
}

function getData(userName, index) {
  var url1 = "https://wind-bow.gomix.me/twitch-api/streams/" + name + "?callback=?"
  var url2 = "https://wind-bow.gomix.me/twitch-api/channels/" + name + "?callback=?"

  $.ajax({
    type: "GET",
    url: url1,
    dataType: "json",
    async: true,
    success: function(streamed_data) {
      streams[index] = streamed_data;
      done();
    }
  });

  $.ajax({
    type: "GET",
    url: url2,
    dataType: "json",
    async: true,
    success: function(channel_data) {
      channels[index] = channel_data;
      done();
    }
  });
}

for (var i = 0; i < user_arr.length; i++) {
  name = user_arr[i];
  getData(name, i);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Your logs look weird because the array WAS empty when you logged it, but the devtools are reflecting the changes happening later if you log the entire array instead of the length.

Rialgar
  • 753
  • 5
  • 9
  • It does address this by explicitely passing the index the result should be put under into the 'getData' function instead of using push. The orders WILL be the same. – Rialgar May 25 '18 at 11:04
  • You're right, I missed that - my mistake. However the calling of `done()` in each request of each iteration is a little excessive. – Rory McCrossan May 25 '18 at 11:08
  • I agree, but I dislike sorting after the fact, the ideal solution would create an array of promises and use Promise.all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all , but alas, the question asked for a solution without promises – Rialgar May 25 '18 at 11:12
  • Thanks @Rialgar, for your reply, I would really to see the ideal solution – Rajat Audichya May 25 '18 at 12:53
  • will create it as a separate answer, just a sec. – Rialgar May 25 '18 at 12:54
0

Separate answer that includes the nicest use of promises I can come up with.

Note: Promise is supported by everyone except Internet Explorer (yes, even Edge). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

I don't know how to map this onto the JQuery $.when, so if you need IE support you need to figure that out.

var user_arr = ["storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]

function getStreams(userName) {
  var url1 = "https://wind-bow.gomix.me/twitch-api/streams/" + userName + "?callback=?" ; 
  return $.ajax({
    type: "GET",
    url: url1,
    dataType: "json",
    async: true
  });
}
  
function getChannels(userName) {
  var url2 = "https://wind-bow.gomix.me/twitch-api/channels/" + userName + "?callback=?";
  return $.ajax({
    type: "GET",
    url: url2,
    dataType: "json",
    async: true
  });
}

var streamPromise = Promise.all(user_arr.map(getStreams))
var channelPromise = Promise.all(user_arr.map(getChannels))

Promise.all([streamPromise, channelPromise]).then(function(results){
  const streams = results[0];
  const channels = results[1];
  
  //do what you need to do
  console.log('users:', user_arr);
  console.log('streams:', streams);
  console.log('channels:', channels);
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Rialgar
  • 753
  • 5
  • 9
  • Just for reference, this is fine for a small number of users, if you have a large number of users and want to display the results as soon as they come in the code will become a little more tricky – Rialgar May 25 '18 at 13:35