简体   繁体   中英

JavaScript Wait until all async calls finish

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);
})

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);
    });
})

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>

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);
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM