Reputation: 423
I need some help with handling async calls in JavaScript. I have a for loop, each loop calls an async HttpRequest, and adds its response to an array. I want the program to wait until all the async calls are finished before proceeding without jQuery (which is only used for DOM manipulation). I've searched quite bit for solutions but none really worked without heavily changing my code or relying on jQuery.
function httpGet(theUrl, callback) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4 && xmlRequest.status == 200) {
callback(xmlRequest.responseText);
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
(function(urls, data) {
urls.forEach(function(url) {
function(resolve, reject) {
httpGet(url, function(response) {
data.push(JSON.parse(response));
})
};
})
})(urls, data);
// Continue after all async calls are finished
})
UPDATED: Edited with Promise, but still not working, maybe I did something wrong.
function httpGet(theUrl, callback) {
return new Promise(function(resolve, reject) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4 && xmlRequest.status == 200) {
callback(xmlRequest.responseText);
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
})
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
var promises = [];
(function(urls, data) {
urls.forEach(function(url) {
var promise = httpGet(url, function(response) {
data.push(JSON.parse(response));
});
promises.push(promise);
})
Promise.all(promises).then(function() {
console.log(data);
})
})(urls, data);
})
Upvotes: 2
Views: 5238
Reputation: 664196
With promises, you should not use a callback
parameter. Call the resolve
/reject
functions from the promise instead.
Instead of passing a callback to the call, chain the things you want to do with the result of the promise in a .then
handler.
function httpGet(theUrl) {
return new Promise(function(resolve, reject) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4) {
if (xmlRequest.status == 200)
resolve(xmlRequest.responseText);
// ^^^^^^^
else
reject(new Error(xmlRequest.statusText)); // or something
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
});
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx", "RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var promises = urls.map(function(url) {
// ^^^ simpler than forEach+push
var promise = httpGet(url); // <-- no callback
return promise.then(JSON.parse);
});
Promise.all(promises).then(function(data) {
// ^^^^
console.log(data);
});
})
Upvotes: 2
Reputation: 778
Since you are using jQuery you can use the Deferred Object to chain promises.
Collect all the promises and use $.when
with spread operator to wait for all promises to resolve. You can use then
to run a function after all ajax requests are resolved.
ES5 Example
$(document).ready(function () {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx", "RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff"];
var urls = channels.map(function (x) {
return "https://api.twitch.tv/kraken/channels/" + x;
});
var data = [];
var promises = urls.map(function (url) {
return $.get(url).then(function (response) {
data.push(response);
});
});
$.when.apply($, promises).then(function () {
console.log('done', data);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
ES6 Example
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
var promises = urls.map((url) => $.get(url).then((response) => {
data.push(response);
}));
$.when(...promises).then(function() {
console.log('done', data);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Upvotes: 0
Reputation: 1281
Can't it be done by just keeping the count of ajax requests as a variable:
var urls_count, data_count = 0;
function httpGet(theUrl, callback, onComplete) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4 && xmlRequest.status == 200) {
callback(xmlRequest.responseText);
}
if(xmlRequest.readyState == 4){
data_count += 1
if(urls_count == data_count){
//this is called when all ajax calls complete
onComplete();
}
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
urls_count = urls.length;
var onComplete = function(){
//your code after all ajax completes.
}
(function(urls, data) {
urls.forEach(function(url) {
function(resolve, reject) {
httpGet(url, function(response) {
data.push(JSON.parse(response));
}, onComplete)
};
})
})(urls, data);
})
Upvotes: 0