2014-07-16 122 views
6

我想在循环中发送post请求。 如果我发送例如连续2个请求,只有最后一个请求确实进行了回调。在for循环中发送post请求

我做错了什么?

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

你可以在浏览器网络选项卡中检查,那真的只有一个请求被发送? – harishr

+0

@先生问题请花你的时间来审查答案,并选择正确的答案 – domokun

回答

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()方法替换为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 

正如你可以看到,我们已经有了一个错误和九“凯蒂猫”连续输出:)我之前更改上面的函数让我们来看看两个重要事情

首先

onreadystatechange事件商店每次readyState属性更改时自动调用函数或引用,而status属性保存XMLHttpRequest对象的状态。

readyState属性的可能值

  • 0:请求未初始化
  • 1:服务器连接建立
  • 2:请求接收
  • 3:处理请求
  • 4:请求完成和响应准备就绪

status属性可能值

  • 200:OK
  • 404:找不到

正如我在评论中说网页,jsfiddle.net是不可靠的测试异步片段没有一些变化。换句话说,GET方法应更改为POSTurl属性必须改变这个链接/echo/html/(更多选项看看jsFiddle documentation

现在,让我们改变从上面的例子(并按照意见代码

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

第一输出内)://六个输出

(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) 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的值保持不变。

第三输出://在取消对线在上面的例子中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 

所以,在取消保持i为参数的函数后,我们将看到该值i已保存。但是由于有六个输出,所以这仍然不正确,我们只需要三个输出。由于我们并不需要的readyState所有的值或XMLHttpRequest对象的status属性,我们将取消注释所需的第四输出的两行

第四输出://取消注释线在上面showned的4RD输出后例(b) - 最后三个输出

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

最后,这应该是片断的正确版本,这是我们需要的。

另一个全能的万能机制(如我之前比喻说的)将是bind()函数,我不喜欢它,因为它比闭包慢。

+0

hex494D49,你可能是对的,但我怀疑这个答案特别开明OP。问题先生,问题是(我们认为)你没有告诉我们的'// DOING SOMETHING'部分包含变量'i'。但是它在回调函数中,直到'for'循环结束之后才会运行;然后'i == this.selectedAvailableUnits.length',而不管它的原始值如何。在这个闭包中,有一个新的变量叫做'i',它实际上与原来的'i'分开。它只是在函数中声明的,所以它的值在迭代循环时不会被重置。 –

+0

@David Knipe把'i'关掉,并尝试连续三次回声'echo“Hello Kitty”'没有关闭:)在这种情况下,'i'不重要。 – hex494D49

+0

再次想到我不认为我们彼此了解。你是否说过,如果函数被一个普通的代码块所取代,那么''Hello Kitty''的最后一个例子将不起作用?我不同意。你为什么这么认为?你有一个jsfiddle来展示它吗? –

2

对不起,我不angularjs工作,但是这两种方法是POST使用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); 
}); 
1

您正在尝试使用for-loop内的变化变量url

如果在循环中不使用闭包,则只有for的最后一个值会使其呼叫$http.post
圈内的封闭可能是一个棘手的野兽。看到这个问题JavaScript closure inside loops – simple practical example谷歌它更多的理论/细节。

您的代码将在某事进行调整如下:

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

编辑:额外的参考
我有一个非常类似的问题,不久前,你可以在这里读到它:Angular JS - $q.all() tracking individual upload progress

1

这显然是一个封闭问题。 阅读全文here

此外,建议使用$ resource over $ http。 (NG-资源)。

查看示例以使用资源发布for循环。

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

      lsProductionService.post({}, postSuccessCallback); 
     }