My objective is to get data from API using $.ajax
call, push each successful data into arrays, and after getting all data (all ajax stops), I append them to the html page.
While the code below here has satisfied my goal by using $(document).ajaxStop()
, I want to ask if there is any different way to wait until all ajax finishes in a loop from i to n times? The point is in the future, maybe I only need all ajax in the loop stops/finishes to get some data, do something with it, then call some ajax after that, but not when all ajax in my whole script stops? (the use of ajaxStop()
to me may not be useful at all times).
$(document).ready(function() {
var streamList = ["HSdogdog", "AdmiralBulldog", "vanNDota", "DreamLeague", "WubWoofWolf", "17uu", "lamperkat", "febbydoto", "Arteezy", "AmazHS", "Moonmeander", "WagamamaTV", "dotademon", "feardota", "esl_dota2", "monkeys_forever", "Dendi", "sheevergaming", "BigDaddy", "Draskyl", "Chessie", "FollowAkke", "bOne7", "attackerdota", "noobfromua", "egm", "qojqva", "alohadancetv", "pajkattdota", "dotastarladder_en", "barnyyy", "zai", "aui_2000", "universedota", "ppd", "fogged", "purgegamers", "meraclechamlotte", "7ckngmad", "miracle_doto", "puppey", "h4nn1", "420jenkins", "illidanstrdoto", "sumaildoto", "jeraxai", "grandgrant", "jeyodude", "bananaslamjamma", "yawar_", "merlinidota", "beyondthesummit", "s4", "stormspirittv", "synderen", "funn1k", "emperorpenguin83", "kano", "miserytheslayer", "reimudesu", "shigetora", "matumbaman", "era17", "sneykingdota", "siractionslacks", "moodota2", "dizzykitten", "sakurafrost225", "Rafis0", "odpixel", "d47biryu", "vampyrette", "w33haa", "blitzdota", "ritsugamer", "limmp", "himegurecia", "dotamajor", "babyknight", "ccncdota2", "lil_hardy", "angelsimosu", "vankhoahoang", "resolut1ontv", "blackdotatv", "sing_sing", "midone", "eternalenvyy", "dotacapitalist", "keemerah", "osu_HDHR", "mssdota", "epicenter_en1", "koushudota", "yapzordota", "moonducktv", "pikachama", "qSnake_", "stan_king", "dota2fata", "madaradota2", "braxton911", "cr1tdota", "mikah138", "evilgeniuses", "dotagasm", "official_niqua", "arise_3012", "nooneboss", "forev", "ramzesdoto", "aliastar", "bububu", "0timado0", "envybaer", "smashdota", "nurbika", "brinkdota", "canceldota", "qodota2", "day9tv", "zingle313", "kvhdota", "abed_dota", "ohaiyodota", "boris_dota", "gorgcc", "ltt98", "pgl_dota", "spare", "hfndota", "happystick", "itshafu", "thijshs", "qsnake", "lizzarddota2", "ek0p", "godot", "chainsito11", "freecodecamp", "aepicenter_en2", "Clarkeezy", "NoctisAK47", "Solitary_Judge"];
var online = [];
var offline = [];
var k = 0;
var promise = [];
var promise2 = [];
for (let i = 0; i < streamList.length; i++) {
var request = $.ajax({
url: "https://api.twitch.tv/kraken/streams/" + streamList[i] + "?client_id=rznf9ecq10bbcwe91n6hhnul3dbpg9",
dataType: 'json'
});
promise.push(request);
request.done(function(data) {
if (data.stream !== null) {
online[k++] = {
channel: data.stream.channel.display_name,
game: data.stream.game,
title: data.stream.channel.status,
viewer: data.stream.viewers,
icon: data.stream.channel.logo
};
} else if (data.stream === null) {
var request2 = $.ajax({
url: "https://api.twitch.tv/kraken/channels/" + streamList[i] + "?client_id=rznf9ecq10bbcwe91n6hhnul3dbpg9",
dataType: 'json'
});
promise2.push(request2);
request2.done(function(data2) {
offline.push({
channel: data2.display_name,
game: data2.game,
title: data2.status,
viewer: "Offline",
icon: data2.logo
});
});
}
});
}
$.when.apply($, promise).then(function() {
$.when.apply($, promise2).then(function() {
console.log("this is the final line of console.log");
$("table").addClass("animated fadeIn");
$(".loading").detach();
online.sort(function(a, b) {
if (a.viewer > b.viewer) {
return -1;
} else if (a.viewer < b.viewer) {
return 1;
}
return 0;
});
$("thead").append("<tr><th style='width:25%' class = 'channel'>Channel</th><th class = 'game' style = 'width:20%'>Game</th><th>Title</th><th style = 'width: 15%'>Viewers</th></tr>");
for (var i = 0; i < online.length; i++) {
if (online[i].icon !== null) {
$(".table").append("<tr><td class='name'><img class ='icon' src ='" + online[i].icon + "'>" + online[i].channel + "</td><td>" + online[i].game + "</td><td>" + online[i].title + "</td><td><span id='red'></span> " + online[i].viewer + "</td></tr>");
} else if (online[i].icon === null) {
$(".table").append("<tr><td class='name'><img class ='icon' src='https://i.imgur.com/0C7WUis.png'>" + online[i].channel + "</td><td>" + online[i].game + "</td><td>" + online[i].title + "</td><td><span id='red'></span> " + online[i].viewer + "</td></tr>");
}
}
for (var j = 0; j < offline.length; j++) {
if (offline[j].icon !== null) {
$(".table").append("<tr><td class='name' style='color:#B2ADBF'><img class ='icon-offline' src ='" + offline[j].icon + "'>" + offline[j].channel + "</td><td style='color:#B2ADBF'><em>" + offline[j].game + "</em></td><td style='color:#B2ADBF'>" + offline[j].title + "</td><td style='color:#B2ADBF'>" + offline[j].viewer + "</td></tr>");
} else if (offline[j].icon === null) {
$(".table").append("<tr><td class='name' style='color:#B2ADBF'><img class ='icon-offline' src='https://i.imgur.com/0C7WUis.png'>" + offline[j].channel + "</td><td style='color:#B2ADBF'><em>" + offline[j].game + "</em></td><td style='color:#B2ADBF'>" + offline[j].title + "</td><td style='color:#B2ADBF'>" + offline[j].viewer + "</td></tr>");
}
}
$("tbody tr").click(function() {
window.open("https://www.twitch.tv/" + $(this).find('td.name').text());
});
});
});
});
EDIT 1: After browsing through many topics related to $.when(passing array of deferreds) and also how to deal with nested AJAX calls, I have finally achieved what I want (I edited my snippet code, basically using a nested $.when and you pass array of promises inside). However, a new problem arises, my code works super well only if all promises resolved (all AJAX call returns data succesfully). In the case of a failed AJAX call, i.e. data not found after AJAX call returns, the inner promises array will not resolve, hence never append the data into HTML page. So I have tried changing $.when.apply($, promise2).then(function())
to $.when.apply($, promise2).always(function()
, however, the always
doesn't seem to work as I want since as soon as a AJAX calls return error, it will append the data to my HTML page, hence missing data will be requested after appending is done, so the console.log("This is the final line ...")
that I have tried as an indicator doesn't appear at the final line anymore. Am I missing something with .always().
since it does not wait for $.when()
to ensure all promises have been either rejected or resolved, rather it runs when any promises got rejected.
EDIT 2: I have finally solved my problem if anyone is still interested. The $.always()
that I misunderstood above was actually working as intended, if any promises got rejected, it will execute the function after $.always()
nonetheless even if there are still more promises pending in the array. So thanks to this comment, what I did is basically map all promises of promise2 array (array that will have failed promises) to resolve state whenever it is called. This is the snippet:
promise2 = $.map(promise2, function (p) {
var dfd = $.Deferred();
p.always(function () {
dfd.resolve();
});
return dfd.promise();
}