2016-01-19 49 views
5

我试图用$ q.all来等待所有的承诺都解决了,但在第一次承诺完成后调用!

我在做什么错了?

function sendAudits(audits) { 
    var promises = []; 

    $scope.sendAudits = { 
     progress: 0 
    }; 
    angular.forEach(audits, function (audit, idAudit) { 
     promises.push(saveAudit(audit)); 
    }); 

    $q 
     .all(promises) 
     .then(function (data) { 
      console.log(data); 
     }, function (errors) { 
      console.log(errors); 
     }); 
} 

function saveAudit(audit) { 
    var filename = audit.header.id + ".txt"; 

    return $http({ 
     method: 'PUT', 
     url: '/audits/audits.php?filename=' + encodeURIComponent(filename), 
     data: AuditSvc.getPlainAudit(audit.header.id) 
    }).finally(function() { 
     $scope.sendAudits.progress += 1; 
     console.log("FINALLY: " + audit.header.id); 
    }); 
} 

编辑

分析一个稍微深一点的问题,当一些答复是错误出现这种情况。例如,当服务器返回header("HTTP/1.0 418 I'm A Teapot: " . $filename);,客户端控制台会是这样:

PUT http://localhost:8182/audits/audits.php?filename=1.txt 418 (I'm A Teapot: 1.txt) 
FINALLY: 1 
Object {data: "", status: 418, config: Object, statusText: "I'm A Teapot: 1.txt"} 
PUT http://localhost:8182/audits/audits.php?filename=2.txt 418 (I'm A Teapot: 2.txt) 
FINALLY: 2 
PUT http://localhost:8182/audits/audits.php?filename=3.txt 418 (I'm A Teapot: 3.txt) 
FINALLY: 3 
PUT http://localhost:8182/audits/audits.php?filename=4.txt 418 (I'm A Teapot: 4.txt) 
FINALLY: 4 
+1

你在控制台中看到什么? – yeouuu

+0

第一次调用saveAudit的响应:第一次$ http调用(审核有4个元素)。奇怪的是,进展得到了'最后'提升4次(每次审核一次)。 – Miquel

回答

2

角文档没有细说,但我相信$q.all()的行为在这种情况下相同的方式ES2015 Promise.all()

如果承诺中的任何一个拒绝,所有承诺立即拒绝承诺的价值拒绝,丢弃所有其他承诺,无论他们是否已经解决。

这里最有可能发生的是至少有一个请求失败。您的日志语句不区分$q.all()是成功还是失败,但如果失败,您将看到的第一个错误是。

查看https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all作为报价的来源。

编辑:

如果你想要得到所有响应,甚至当一些失败,那么你应该在saveAudit添加catch处理程序失败转化为成功响应:

function saveAudit(audit) { 
    var filename = audit.header.id + ".txt"; 

    return $http({ 
     method: 'PUT', 
     url: '/audits/audits.php?filename=' + encodeURIComponent(filename), 
     data: AuditSvc.getPlainAudit(audit.header.id) 
    }).catch(function(error) { 
     return { error:error}; 
    }) 
    .finally(function() { 
     $scope.sendAudits.progress += 1; 
     console.log("FINALLY: " + audit.header.id); 
    }); 
} 

然后您需要检查每个响应以查看它是否包含错误或有效数据。

+0

谢谢,这就是我刚刚注意到的(请参阅问题中的编辑)。你知道有什么方法绕过这种行为吗?另外我也看到,如果所有的承诺都被拒绝,那么catch调用只会被调用一次,而不是所有的错误。至少有一个人会希望错误回调被称为所有错误,而不仅仅是第一个错误回调...... – Miquel

+0

如果你想获得所有的错误,你需要将它们转换成成功。请参阅编辑。 – Duncan

2

正如别人所指出的,$q.all不具有弹性。如果其中一个承诺被拒绝,则$q.all将被拒绝并显示第一个错误。

要创建弹性复合的承诺,那就是等待所有的承诺,完成合格或不合格,在每一个人的承诺,使用.catch承诺转化被拒绝的承诺,一个成功的承诺。

var resilientPromises = []; 

angular.forEach(promises, function(p) { 
    var resilientP = p.catch(function(result) { 
     //return to convert rejection to success 
     return result; 
    }); 
    resilientPromises.push(resilientP); 
}); 

$q.all(resilientPromises).then(function (results) { 
    //process results 
}); 

的两件事情,从这个答案带走:

  1. 一个$q.all承诺是没有弹性。它被第一个被拒绝的承诺拒绝。
  2. 履行的承诺可以由被拒绝的承诺创建,将方法或.catch方法的onRejected函数的值返回
+0

+1为弹性解决方案。我已经解决了另一个'$ q.defer()',在任何情况下都解决了,但使用'return'子句我认为它更简单。 – Miquel

+0

它应该是'angular.forEach' –

+0

@ pro.mean谢谢你,我编辑了答案 – georgeawg