2016-01-19 58 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