Sending a request to send in a for loop

I want to send send requests in a loop. If I send, for example, 2 requests per line, only the last request really made a callback.

What am I doing 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); }); } }; 
+6
javascript angularjs for-loop
source share
4 answers

Use 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 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 not needed) } 

The section below is not directly related to the question, but rather the comments related to this answer.


The example presented in jsFiddle mentioned in the comments and below is an error and as such does not prove anything.

It is true that this snippet, without even using closure, yields "Hello Kitty" three times; in fact, if you replace the console.log() method with alert() , you will see that it gives "Hello Kitty" six, nine, or even twelve times. So, what the hell is going on alone;) how can I get a warning window that appears six, nine or twelve times in a cycle 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 can see, we have an error and nine "Hello Kitty" outputs per line :) Before I change the function described above, we will see two important things

First

onreadystatechange event stores a function or reference that should be called automatically each time the readyState property changes, and the status property contains the status of the XMLHttpRequest object.

readyState Possible property values

  • 0: request not initialized
  • 1: server connection established
  • 2: received request
  • 3: processing request
  • 4: The request is complete and the response is ready.

status Possible property values

  • 200: OK
  • 404: Page not found

Second

As I said in the comments, jsfiddle.net is not reliable for testing asynchronous fragments without any changes. In other words, the GET method must be changed to POST , and the url property must be changed to this link /echo/html/ (for more options take a look at jsFiddle )

Now change the example from the above (and follow the comments in 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 have two different outputs. As you can see, different outputs for different ReadyState property values ​​were outputs. But the value of i remained the same.

Third conclusion: // after uncommenting the lines for the 3rd output shown above in 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 that contains i as an argument, we see that the value of i was saved. But this is still not true, since there are six outputs, and we need only three. Since we do not need all the values ​​of the readyState property or status of the XMLHttpRequest object, let it uncomment the two lines needed for the fourth output

4th output: // after uncommenting the lines for the 4th output shown above in example (b), finally three outputs

 i=0 - readyState=4 - Hello Kitty i=1 - readyState=4 - Hello Kitty i=2 - readyState=4 - Hello Kitty 

Finally, it must be the correct version of the fragment, and this is what we need.

Another omnipotent omnipotent mechanism (as I expressively said earlier) was the bind() function, which I do not prefer, since it is slower than closure.

+4
source share

Sorry, I do not work with angularjs, but these two methods that publish using jQuery or even the basic 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); }); 
+2
source share

You are trying to use a variable url variable inside a for-loop .

If you do not use closure inside the loop, only the last value of your for will result in a call to $http.post .
Closing inside loops can be a tricky beast. See This question Closing JavaScript inside loops is a simple practical example and google for more theory / details.

Your code should be adjusted something like this:

 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: sitelink
I had a very similar problem recently and you can read about it here: Angular JS - $ q.all () tracking individual download results

+1
source share

Its clearly a closing problem. More here

He also suggested using $ resource over $ http. (Ng-resource).

See an example to send a message in a loop using a resource.

  for(var i=0; i<$scope.ListOfRecordsToPost.length; i++){ var postSuccessCallback = function(postRec) { console.info('Posted ' + postRec); }($scope.ListOfRecordsToPost[i]); lsProductionService.post({}, postSuccessCallback); } 
+1
source share

All Articles