我想循环发送帖子请求。 如果我连续发送2个请求,那么只有最后一个请求真的进行了回调。
我做错了什么?
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);
});
}
};
答案 0 :(得分:5)
使用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
正如你所看到的,我们有一个错误和连续九个'Hello Kitty'输出:)在我改变上面的功能之前,让我们看看两个重要的事情
<强>第一强>
onreadystatechange
事件存储每次readyState
属性更改时自动调用的函数或引用,而status
属性保存XMLHttpRequest对象的状态。
readyState
属性可能值
status
属性可能值
<强>第二强>
正如我在评论中所说,如果没有一些更改,jsfiddle.net对于测试异步代码段是不可靠的。换句话说,GET
方法应更改为POST
,并且url
属性必须更改为此链接/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属性值的不同输出已经产生。
但i
的价值保持不变。
第三个输出://取消注释示例(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
对象的status
或XMLHttpRequest
属性的所有值,因此我们取消注释第四个输出所需的两行
第4个输出://取消注释示例(b)中上面显示的第4个输出的行 - 最后三个输出
i=0 - readyState=4 - Hello Kitty
i=1 - readyState=4 - Hello Kitty
i=2 - readyState=4 - Hello Kitty
最后,这应该是片段的正确版本,这就是我们需要的。
另一种万能的,无所不能的机制(正如我之前所说的那样)将是bind()
函数,我不喜欢它,因为它比闭包慢。
答案 1 :(得分:2)
很抱歉,我不使用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>
服务器代码(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 :(得分:1)
您正尝试在url
内使用更改变量for-loop
。
如果您不在循环中使用闭包,则只有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
答案 3 :(得分:1)
这显然是一个封闭问题。 阅读更多here
此外,它建议使用超过$ http的$ resource。 (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);
}