简体   繁体   English

在for循环中发送post请求

[英]Sending post request in for loop

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. 如果我连续发送2个请求,那么只有最后一个请求真正进行了回调。

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 . 使用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. 在评论中提到的jsFiddle提交的示例如下所示是错误的,因此它没有任何证据。

It's true that this snippet, even not using a closure, yields 'Hello Kitty' three times; 确实,这个片段,即使不使用封闭,也会产生三次“Hello Kitty”; 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. 实际上,如果用一个alert()替换console.log()方法,你会看到它产生'Hello Kitty'六,九,甚至十二次。 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 正如你所看到的,我们有一个错误和连续九个'Hello Kitty'输出:)在我改变上面的功能之前让我们看看两个重要的事情

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. onreadystatechange事件存储每次readyState属性更改时自动调用的函数或引用,而status属性保存XMLHttpRequest对象的状态。

readyState property possible values readyState属性可能的值

  • 0: request not initialized 0:请求未初始化
  • 1: server connection established 1:建立服务器连接
  • 2: request received 2:收到请求
  • 3: processing request 3:处理请求
  • 4: request finished and response is ready 4:请求完成并且响应准备就绪

status property possible values status属性可能的值

  • 200: OK 200:好的
  • 404: Page not found 404页面不存在

Second 第二

As I said in the comments, jsfiddle.net isn't reliable for testing asynchronous snippets without some changes. 正如我在评论中所说,如果没有一些更改,jsfiddle.net对于测试异步代码段是不可靠的。 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 ) 换句话说,应该将GET方法更改为POST并且必须将url属性更改为此链接/echo/html/ (有关更多选项,请查看jsFiddle文档

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. 如果没有在例子(b)中做出任何改变,我们有两个不同的输出。 As you can see, different outputs for different readyState property values has been yield. 如您所见,不同readyState属性值的不同输出已经产生。 But the value of i remained the same. i的价值保持不变。

3rd output: // after uncommenting the lines for the 3rd output showned above in the example (b) 第3个输出://取消注释示例(b)中上面显示的第3个输出的行

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. 因此,在取消注释将i作为参数的函数后,我们看到i的值已被保存。 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 因为我们不需要XMLHttpRequest对象的readyStatestatus属性的所有值,所以让我们取消注释第四个输出所需的两行

4th output: // after uncommenting the lines for the 4rd output showned above in the example (b) - finally three outputs 第4个输出://取消注释示例(b)中上面显示的第4个输出的行 - 最后是三个输出

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. 另一个全能的,无所不能的机制(正如我之前所说的那样)将是bind()函数,我不喜欢它,因为它比闭包慢。

Sorry, I don't work with angularjs, but these two methods that post using jQuery or even base XMLHttpRequest work well for me: 对不起,我不使用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>

Server code (nodejs): 服务器代码(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);
});

You're trying to use a changing variable, url , inside a for-loop . 你试图在for-loop使用一个变化的变量url

If you don't use a closure inside a loop, only the last value of your for will made it to the $http.post call. 如果你不在循环中使用闭包,只有你的for的最后一个值将进入$http.post调用。
Closures inside loops can be a tricky beast. 循环内部的闭包可能是一个棘手的野兽。 See this question JavaScript closure inside loops – simple practical example and google it for more theory/details. 看到这个问题JavaScript封闭内部循环 - 简单实用的例子和谷歌它更多的理论/细节。

Your code will have to be adjusted in 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: additional reference 编辑:附加参考
I had a very similar issue not long ago, and you can read about it here: Angular JS - $q.all() tracking individual upload progress 我不久前有一个非常类似的问题,你可以在这里阅读: Angular JS - $ q.all()跟踪个人上传进度

Its clearly a closure issue. 这显然是一个封闭问题。 Read more here 在这里阅读更多

Also it's suggested using $resource over $http. 此外,建议使用超过$ http的$ resource。 (ng-resource). (NG-资源)。

Check out the example to post in for loop using 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); 
      }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM