Angular $q, Как связать несколько promises внутри и после цикла for
Я хочу иметь цикл for, который вызывает асинхронные функции для каждой итерации.
После цикла for я хочу выполнить другой блок кода, но не раньше, чем все предыдущие вызовы в цикле for были решены.
Моя проблема на данный момент заключается в том, что либо код-блок после цикла for выполняется до того, как все асинхронные вызовы закончены ИЛИ он вообще не выполнен.
Часть кода с циклом FOR и блоком кода после него (для полного кода см. fiddle):
[..]
function outerFunction($q, $scope) {
var defer = $q.defer();
readSome($q,$scope).then(function() {
var promise = writeSome($q, $scope.testArray[0])
for (var i=1; i < $scope.testArray.length; i++) {
promise = promise.then(
angular.bind(null, writeSome, $q, $scope.testArray[i])
);
}
// this must not be called before all calls in for-loop have finished
promise = promise.then(function() {
return writeSome($q, "finish").then(function() {
console.log("resolve");
// resolving here after everything has been done, yey!
defer.resolve();
});
});
});
return defer.promise;
}
Я создал jsFiddle, который можно найти здесь http://jsfiddle.net/riemersebastian/B43u6/3/.
В настоящий момент похоже, что порядок выполнения в порядке (см. вывод консоли).
Я предполагаю, что это просто потому, что каждый вызов функции немедленно возвращается без какой-либо реальной работы. Я попытался отложить defer.resolve с setTimeout, но не удалось (т.е. Последний блок кода никогда не выполнялся). Вы можете видеть это в разбитом блоке в скрипке.
Когда я использую реальные функции, которые записывают в файл и читают из файла, последний блок кода выполняется до завершения последней операции записи, чего я не хочу.
Конечно, ошибка может быть в одной из этих функций чтения/записи, но я хотел бы подтвердить, что нет ничего плохого в коде, который я разместил здесь.
Ответы
Ответ 1
Что вам нужно использовать, это $q.all, который объединяет несколько promises в один, который разрешен только тогда, когда все promises.
В вашем случае вы можете сделать что-то вроде:
function outerFunction() {
var defer = $q.defer();
var promises = [];
function lastTask(){
writeSome('finish').then( function(){
defer.resolve();
});
}
angular.forEach( $scope.testArray, function(value){
promises.push(writeSome(value));
});
$q.all(promises).then(lastTask);
return defer.promise;
}
Ответ 2
С новым ES7 вы можете получить тот же результат гораздо более простым способом:
let promises = angular.forEach( $scope.testArray, function(value){
writeSome(value);
});
let results = await Promise.all(promises);
console.log(results);
Ответ 3
Вы можете использовать $q
и 'уменьшить' вместе, чтобы связать promises.
function setAutoJoin() {
var deferred = $q.defer(), data;
var array = _.map(data, function(g){
return g.id;
});
function waitTillAllCalls(arr) {
return arr.reduce(function(deferred, email) {
return somePromisingFnWhichReturnsDeferredPromise(email);
}, deferred.resolve('done'));
}
waitTillAllCalls(array);
return deferred.promise;
}
Ответ 4
Это сработало для меня, используя синтаксис ES5
function outerFunction(bookings) {
var allDeferred = $q.defer();
var promises = [];
lodash.map(bookings, function(booking) {
var deferred = $q.defer();
var query = {
_id: booking.product[0].id,
populate: true
}
Stamplay.Object("product").get(query)
.then(function(res) {
booking.product[0] = res.data[0];
deferred.resolve(booking)
})
.catch(function(err) {
console.error(err);
deferred.reject(err);
});
promises.push(deferred.promise);
});
$q.all(promises)
.then(function(results) { allDeferred.resolve(results) })
.catch(function(err) { allDeferred.reject(results) });
return allDeferred.promise;
}