Отправка запроса post в цикл for
Я хотел бы отправить запросы post в цикле. Если я отправляю, например, 2 запроса подряд, только последний запрос действительно сделал обратный вызов.
что я делаю не так?
this.assignAUnits = function(){
var currentIncidentId = this.incident.incidentId;
for (var i=0; i< this.selectedAvailableUnits.length; i++){
var unit = this.selectedAvailableUnits[i];
var unitId = unit.unitId;
var url = '/incident/' + currentIncidentId + '/assignUnit/' + unitId
$http.post(url).then(function(response) {
DOING SOMETHING
}, function(error) {
alert(error);
});
}
};
4 ответов
использовать closure
. Позвольте мне показать вам простой пример
// JavaScript on Client-Side
window.onload = function() {
var f = (function() {
for (i = 0; i < 3; i++) {
(function(i){
var xhr = new XMLHttpRequest();
var url = "closure.php?data=" + i;
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText); // 0, 1, 2
}
};
xhr.send();
})(i);
}
})();
};
// Server-Side (PHP in this case)
<?php
echo $_GET["data"];
?>
в вашем случае... оберните асинхронный вызов / функцию с замыканием
for (var i=0; i< this.selectedAvailableUnits.length; i++) {
(function(i) { // <--- the catch
var unit = this.selectedAvailableUnits[i];
var unitId = unit.unitId;
var url = '/incident/' + currentIncidentId + '/assignUnit/' + unitId
$http.post(url).then(function(response) {
// DOING SOMETHING
}, function(error) {
alert(error);
});
})(i); // <---- (the i variable might be omitted if it's not needed)
}
раздел ниже напрямую не связан с вопросом, а скорее с комментариями, связанными с этим ответом.
пример представлен на jsFiddle упомянутый в комментариях и показанный ниже багги и как таковой он ничего не доказывает.
это правда, что этот фрагмент, даже не используя закрытие, дает "Hello Kitty" три раза; на самом деле, если вы замените console.log()
метод с an alert()
один вы увидите, что он дает "Hello Kitty" шесть, девять или даже двенадцать раз. Итак, что, черт возьми, происходит один ;) как можно заставить окно оповещения выскакивать шесть, девять или двенадцать раз в цикле из трех итераций?!
// your example (a) // my comments
//
var f = (function() {
for (i = 0; i < 3; i++) {
//(function(){ // this way you can't have multiple scopes
var xhr = new XMLHttpRequest();
var url = "closure.php?data=your-data"; // use /echo/html/ for testing on jsfiddle.net
xhr.open("GET", url, true); // use POST for testing on jsfiddle.net
xhr.onreadystatechange = function () { // this way you might catch all readyStage property values
callback(); // this way the callback function will be called several times
};
xhr.send();
//})();
}
})();
var callback = function() {
console.log("Hello Kitty"); // or use alert("Hello Kitty");
};
выход:
GET http://fiddle.jshell.net/_display/closure.php?data=your-data 404 (NOT FOUND)
(9) Hello Kitty
как вы могли видеть, у нас ошибка и девять выходов "Hello Kitty" подряд :) прежде чем я изменю функцию выше, давайте посмотрим две важные вещи
первый
onreadystatechange
событие хранит функцию или ссылку, которая будет вызываться автоматически каждый раз, когда readyState
изменения свойств, в то время как status
свойство содержит состояние объекта XMLHttpRequest.
readyState
возможные значения свойств
- 0: запрос не инициализирован
- 1: установлено соединение с сервером
- 2: запрос получен
- 3: обработка запроса
- 4: запрос завершен, и ответ готов
status
возможные значения свойств
- 200: OK
- 404: страница не найдена
второй
как я сказал в комментариях, jsfiddle.net не является надежным для тестирования асинхронных фрагментов без небольшие изменения. Другими словами,GET
метод должен быть изменен на POST
и url
свойство должно быть изменено на эту ссылку /echo/html/
(для больше вариантов взгляните на документация jsFiddle)
теперь давайте изменим пример из приведенного выше (и следуйте комментариям в коде)
// corrected example (b)
//
var f = (function() {
for (i = 0; i < 3; i++) {
//(function(i){ // uncomment this line for the 3rd output
var xhr = new XMLHttpRequest();
var url = "/echo/html";
var data = "data";
xhr.open("POST", url, true);
xhr.onreadystatechange = function () {
//if (xhr.readyState == 4 && xhr.status == 200) { // uncomment this line for the 4th output
callback(i, xhr.readyState); // uncomment this line for the 4th output
//}
};
xhr.send(data);
//})(i); // uncomment this line for the 3rd output
}
})();
var callback = function(i, s) {
console.log("i=" + i + " - readyState=" + s + " - Hello Kitty");
};
1-й выход: / / шесть выходов
(4) i=3 - readyState=1 - Hello Kitty // four outputs related to readyState value 'server connection established'
i=3 - readyState=2 - Hello Kitty // related to readyState value 'request received'
i=3 - readyState=4 - Hello Kitty // related to readyState value 'request finished and response is ready'
2-й выход: // шесть выходов
(2) i=3 - readyState=1 - Hello Kitty // two outputs related to readyState value 'server connection established'
i=3 - readyState=2 - Hello Kitty // related to readyState value 'request received'
(3) i=3 - readyState=4 - Hello Kitty // three outputs related to readyState value 'request finished and response is ready'
без каких-либо изменений, внесенных в пример (b), у нас есть два разных выхода. Как вы можете видеть, разные выходы для разных значений свойств readyState были yield.
Но значение i
остались прежними.
3-й выход: / / после раскомментирования строк для 3-го выхода, показанного выше в примере (b)
i=0 - readyState=2 - Hello Kitty // related to readyState value 'request received'
i=0 - readyState=4 - Hello Kitty // related to readyState value 'request finished and response is ready'
i=1 - readyState=2 - Hello Kitty // ...
i=1 - readyState=4 - Hello Kitty // ...
i=2 - readyState=2 - Hello Kitty
i=2 - readyState=4 - Hello Kitty
Итак, после раскомментирования функции, которая содержит i
в качестве аргумента, мы видим, что значение i
была сохранена. Но это все еще неверно, так как есть шесть выходов, а нам нужно только три. Поскольку нам не нужны все значения readyState
или status
свойства XMLHttpRequest
объект, раскомментируем две строки, необходимые для четвертого вывода
4-й выход: / / после раскомментирования строк для 4-го выхода, показанного выше в примере (b) - наконец, три выхода
i=0 - readyState=4 - Hello Kitty
i=1 - readyState=4 - Hello Kitty
i=2 - readyState=4 - Hello Kitty
наконец, это должна быть правильная версия сниппета и это то, что нам нужно.
еще один всемогущий, всемогущий механизм (как я образно сказал) будет bind()
функция, которую я не предпочитаю, так как она медленнее закрытия.
Извините, я не работаю с angularjs, но эти два метода, которые публикуют с помощью jQuery или даже базового XMLHttpRequest, хорошо работают для меня:
<button onclick="sendWithJQuery()">send</button>
<ul id="container"></ul>
<script src="/vendor/bower_components/jquery/dist/jquery.js"></script>
<script>
//use XMLHttpRequest
function send(){
for (var i = 1; i <= 10; i++){
var xhr = new XMLHttpRequest();
xhr.open('POST', '/test/' + i);
xhr.onreadystatechange = function(){
if (this.readyState != 4){
return;
}
var li = document.createElement('li');
li.appendChild(document.createTextNode('client time:' + new Date().toISOString() + ', data: ' + this.responseText));
container.appendChild(li);
}
xhr.send();
}
}
//use jQuery
function sendWithJQuery(){
for (var i = 1; i <= 10; i++){
$.ajax({
url: '/test/' + i,
method: "POST",
statusCode: {
200: function (data, textStatus, jqXHR) {
var li = document.createElement('li');
li.appendChild(document.createTextNode('client time:' + new Date().toISOString() + ', data: ' + JSON.stringify(data)));
container.appendChild(li);
},
500: function (data, textStatus, jqXHR) {
alert('Internal server error');
}
}
});
}
}
</script>
код сервера (nodejs):
router.post('/test/:i', function(req, res) {
var i = req.params.i;
var t = new Date().toISOString();
setTimeout(function(){
res.send({i: i, t: t});
}, 1000);
});
вы пытаетесь использовать переменную, url
внутри for-loop
.
если вы не используете закрытие внутри петли, только последнее значение вашего for
сделал это $http.post
звонок.
Закрытие внутренних петель может быть хитрым зверем. См. этот вопрос JavaScript закрытие внутри петли – простой практический пример и google для получения дополнительной теории / деталей.
ваш код должен быть скорректирован в чем-то вроде это:
var doPost = function(url) {
$http.post(url).then(
function(response) {
// DOING SOMETHING
},
function(error) {
alert(error);
});
}
this.assignAUnits = function(){
var currentIncidentId = this.incident.incidentId;
for (var i=0; i< this.selectedAvailableUnits.length; i++){
var unit = this.selectedAvailableUnits[i];
var unitId = unit.unitId;
var url = '/incident/' + currentIncidentId + '/assignUnit/' + unitId
doPost(url)
}
};
Edit: дополнительная ссылка
У меня была очень похожая проблема не так давно, и вы можете прочитать об этом здесь: угловой JS - $q.all () отслеживание индивидуального прогресса загрузки
это явно проблема закрытия. Подробнее здесь
также предлагается использовать $resource через $http. (ng-resource).
Проверьте пример для публикации в цикле for, используя ресурс.
for(var i=0; i<$scope.ListOfRecordsToPost.length; i++){
var postSuccessCallback = function(postRec) {
console.info('Posted ' + postRec);
}($scope.ListOfRecordsToPost[i]);
lsProductionService.post({}, postSuccessCallback);
}