2015-12-10 4 views
0

Мне нужна помощь в обработке асинхронных вызовов в JavaScript. У меня есть цикл for, каждый цикл вызывает async HttpRequest и добавляет его ответ массиву. Я хочу, чтобы программа дождалась завершения всех асинхронных вызовов, прежде чем продолжить без jQuery (который используется только для манипуляций с DOM). Я искал довольно много решений, но ни один из них не работал без изменения моего кода или полагался на jQuery.JavaScript Подождите, пока все асинхронные вызовы закончатся

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

ОБНОВЛЕНО: Под редакцией с Promise, но до сих пор не работает, может быть, я сделал что-то не так.

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

Посмотрите в обещания, в частности 'Promise.all'. – elclanrs

+0

@elclanrs Я редактировал код с обещанием, но он все еще не работает. Я еще не привык к использованию обещаний, так что, возможно, я сделал что-то неправильно –

ответ

1

С обещаниями вы не должны использовать параметр callback. Вместо этого вызовите функции resolve/reject.

Вместо того, чтобы передавать обратный вызов на вызов, связывайте вещи, которые вы хотите сделать, с результатом обещания в обработчике .then.

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

Поскольку вы используете JQuery вы можете использовать Deferred Object к цепи обещаний.

Соберите все обещания и используйте $.when с помощью оператора распространения, чтобы дождаться решения всех проблем. Вы можете использовать then для запуска функции после того, как все аякс-запросы будут разрешены.

ES5 Пример

$(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 Пример

$(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>

+0

Почему вы удалили уведомление о необходимости ES6 для этого? – Bergi

+0

Считается, что я добавлю пример, совместимый с ES5. Я предположил, что он использовал ES6 с помощью 'channels.map ((x) =>' code – Rovak

0

Не может это сделать, просто сохраняя количество Аякса запросов в качестве переменного:

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

То, что 'data_count' должно быть локальным для этой функции цикла, не помещается в' httpGet'. – Bergi

+0

AFAIK, 'httpGet' является обычным функция, поэтому в эту функцию всегда можно передать дополнительный параметр (функция, которая должна быть запущена при завершении), которая может быть нулевой в некоторых случаях, а data_count также может быть передан как дополнительный параметр для сравнения. – Raman

+0

Да, конечно, вы может изменять пользовательские функции, но это не значит, что вы должны *. Вы нарушаете повторное использование и инкапсуляцию. – Bergi

Смежные вопросы