2014-07-16 6 views
6

Ich möchte Post-Anfragen in Schleife senden. Wenn ich zum Beispiel 2 Anfragen hintereinander sende, hat nur die letzte Anfrage wirklich den Rückruf gemacht.Post-Anfrage in For-Schleife senden

Was mache ich falsch?

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

können Sie in der Browser-Netzwerk-Registerkarte überprüfen, dass wirklich nur eine Anfrage gesendet wird ?? – harishr

+0

@ Herr-Frage bitte nehmen Sie sich Zeit, um die Antworten zu überprüfen und wählen Sie die richtige – domokun

Antwort

4

ein closure verwenden. Lassen Sie mich Ihnen ein einfaches Beispiel

// 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 Ihrem Fall ... wickeln Sie die asynchrone Aufruf/Funktion mit einem Verschluss

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) 

} 

Im folgenden Abschnitt wird nicht direkt auf die Frage bezogen ist sondern eher zu den Kommentaren zu dieser Antwort.


Das Beispiel auf jsFiddle in den Kommentaren erwähnt abgegeben und beweisen unten ist fehlerhaft und als solche nichts gezeigt.

Es ist wahr, dass dieser Ausschnitt, sogar ohne einen Verschluss, "Hello Kitty" dreimal ergibt; Tatsächlich, wenn Sie console.log() Methode mit einem alert() ersetzen, werden Sie sehen, dass es 'Hello Kitty' sechs, neun oder sogar zwölf Mal ergibt. Also, was zum Teufel wird einer gehen?) Wie ist es möglich, das Warnfenster sechs, neun oder zwölf Mal innerhalb einer Schleife von drei Iterationen zu bekommen ?!

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

Ausgang:

GET http://fiddle.jshell.net/_display/closure.php?data=your-data 404 (NOT FOUND) 
(9) Hello Kitty 

Wie Sie sehen können, haben wir einen Fehler und neun ‚Hallo Kitty‘ gibt in einer Reihe :) bekam Bevor ich die Funktion oben wir zwei wichtige sehen ändern Sache

Erste

onreadystatechange Ereignis speichert eine Funktion oder eine Referenz, die jedes Mal automatisch aufgerufen wird, wenn sich die readyState-Eigenschaft ändert, während die status-Eigenschaft den Status des XMLHttpRequest-Objekts enthält.

readyState Eigenschaft möglichen Werte

  • 0: Server-Verbindung hergestellt
  • : 2: Verarbeitungsanforderung
  • 4:: fordern fertig und Antwortanforderung
  • 3 empfangen
  • 1 nicht initialisiert anfordern ist bereit

status Eigenschaft mögliche Werte

  • 200: OK
  • 404: Seite nicht

Zweite

Wie ich in den Kommentaren, sagte gefunden, sind jsfiddle.net nicht zuverlässig für Testen asynchroner Snippets ohne einige Änderungen. Mit anderen Worten sollte die GET Methode POST und die url Eigenschaft geändert werden muss, um diesen Link /echo/html/ geändert werden (für weitere Optionen zu sehen jsFiddle documentation)

Jetzt wollen wir das Beispiel aus dem oben ändern (und folgen Sie den Kommentaren innerhalb des Codes)

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

ersten Ausgabe: // sechs Ausgänge

(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. Ausgabe: // sechs Ausgänge

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

Ohne irgendwelche Änderungen in Beispiel (b) haben wir zwei verschiedene Ausgänge. Wie Sie sehen können, wurden unterschiedliche Ausgaben für verschiedene readyState-Eigenschaftswerte erzielt. Aber der Wert von i blieb gleich.

3. Ausgabe: // nach der Leitungen für die 3. Ausgabe uncommenting im obigen Beispiel showned (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, nachdem die Funktion uncommenting die i als Argument hält, sehen wir, dass der Wert von i wurde gespeichert. Aber das ist immer noch falsch, da es sechs Ausgänge gibt und wir nur drei brauchen. Wie wir alle müssen die Werte von readyState oder status Eigenschaft des Objekts XMLHttpRequest nicht, lassen Sie sich die zwei Linien für die vierte Ausgabe

4. Ausgabe benötigt Kommentar-: // nach den Leitungen für den 4rd Ausgang uncommenting oben in dem showned Beispiel (b) - schließlich drei Ausgänge

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

Schließlich sollte die korrekte Version des Snippets sein und das ist, was wir brauchen.

Ein anderer allmächtig, allmächtig Mechanismus (wie ich schon bildlich gesagt) wäre die bind() Funktion, die ich lieber nicht, da es als eine Schließung langsamer ist.

+0

hex494D49, Sie haben wahrscheinlich Recht, aber ich bezweifle, dass OP ist besonders durch diese Antwort aufgeklärt. Herr Frage, das Problem ist (denken wir), dass der '// DOING SOMETHING' Teil, von dem Sie uns nicht erzählt haben, die Variable 'i' enthält. Aber es ist in einer Callback-Funktion, die nicht läuft, bis die For-Schleife beendet ist; bis dahin "i == this.selectedAvailableUnits.length", unabhängig von seinem ursprünglichen Wert. In dieser Schließung gibt es eine neue Variable mit dem Namen "i", die eigentlich vom ursprünglichen "i" getrennt ist. Es wird nur innerhalb der Funktion deklariert, so dass der Wert bei der Iteration durch die Schleife nicht zurückgesetzt wird. –

+0

@David Knipe Nimm das "i" ab und versuche dreimal hintereinander zu echo "echo" Hello Kitty "' ohne eine Schließung :) Das 'i' ist in diesem Fall nicht wichtig. – hex494D49

+0

Nachdenklich denke ich nicht, dass wir uns verstehen. Willst du damit sagen, dass das letzte Beispiel mit "Hello Kitty" nicht funktionieren würde, wenn die Funktion nur durch einen normalen Code ersetzt würde? Ich bin nicht einverstanden. Warum denkst du das? Hast du eine Idee, es zu demonstrieren? –

2

Sorry, ich weiß nicht mit AngularJS arbeiten, aber diese beiden Methoden, die jQuery oder auch Basis XMLHttpRequest funktionieren gut für mich einwerfen mit:

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

Sie versuchen, eine sich ändernde Variable url in einem for-loop zu verwenden.

Wenn Sie keine Schließung innerhalb einer Schleife verwenden, wird nur der letzte Wert Ihrer for es auf den $http.post Aufruf gemacht.
Verschlüsse in Schleifen können ein kniffliges Biest sein. Sehen Sie diese Frage JavaScript closure inside loops – simple practical example und googlen Sie es für mehr Theorie/Details.

Ihr Code wird in etwa wie folgt angepasst werden:

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: zusätzliche Referenz
ich vor nicht langer Zeit ein sehr ähnliches Problem hatte, und Sie können darüber lesen Sie hier: Angular JS - $q.all() tracking individual upload progress

1

Es ist eindeutig ein Schließproblem. Lesen Sie mehr here

Es ist auch vorgeschlagen, mit $ Ressource über $ http. (ng-Ressource).

Schauen Sie sich das Beispiel an, das in for loop mit Ressource gepostet werden soll.

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

      lsProductionService.post({}, postSuccessCallback); 
     }