I would like to send post requests in loop. If i send for example 2 requests in a row only the last request really made the callback.
What am i do wrong?
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);
});
}
};
Use a closure
. Let me show you a simple example
// 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"];
?>
In your case... wrap the asynchronous call/function with a closure
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)
}
The section below is not directly related to the question but rather to the comments related to this answer.
The example submitted on jsFiddle mentioned in the comments and shown below is buggy and as such it doesn't prove anything.
It's true that this snippet, even not using a closure, yields 'Hello Kitty' three times; actually, if you replace console.log()
method with the an alert()
one you will see that it yields 'Hello Kitty' six, nine or even twelve times. So, what the hell is going one ;) how it's possible to get the alert window popping up six, nine or twelve times within a loop of three iterations?!
// 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");
};
Output:
GET http://fiddle.jshell.net/_display/closure.php?data=your-data 404 (NOT FOUND)
(9) Hello Kitty
As you could see, we've got an error and nine 'Hello Kitty' outputs in a row :) Before I change the function above let's see two important thing
First
onreadystatechange
event stores a function or a reference to be called automatically each time the readyState
property changes while status
property holds the status of the XMLHttpRequest object.
readyState
property possible values
status
property possible values
Second
As I said in the comments, jsfiddle.net isn't reliable for testing asynchronous snippets without some changes. In other words the GET
method should be changed to POST
and the url
property must be changed to this link /echo/html/
(for more options take a look at jsFiddle documentation)
Now, let's change the example from the above (and follow the comments within the code)
// 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");
};
1st output: // six outputs
(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'
2nd output: // six outputs
(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'
Without any changes made in example (b), we've got two different outputs. As you can see, different outputs for different readyState property values has been yield.
But the value of i
remained the same.
3rd output: // after uncommenting the lines for the 3rd output showned above in the example (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
So, after uncommenting the function which holds i
as an argument, we see that the value of i
has been saved. But this is still incorrect since there are six outputs and we need only three. As we don't need all the values of readyState
or status
property of the XMLHttpRequest
object, let's uncomment the two lines needed for the fourth output
4th output: // after uncommenting the lines for the 4rd output showned above in the example (b) - finally three outputs
i=0 - readyState=4 - Hello Kitty
i=1 - readyState=4 - Hello Kitty
i=2 - readyState=4 - Hello Kitty
Finally, this should be the correct version of the snippet and this is what we need.
Another almighty, omnipotent mechanism (as I figuratively said before) would be the bind()
function which I don't prefer since it's slower than a closure.
Sorry, I don't work with angularjs, but these two methods that post using jQuery or even base XMLHttpRequest work well for me:
<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>
Server code (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);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With