Сделайте angular.forEach ждать обещания после перехода к следующему объекту
У меня есть список объектов. Объекты передаются на отложенную функцию. Я хочу вызвать функцию со следующим объектом только после разрешения предыдущего вызова. Есть ли способ сделать это?
angular.forEach(objects, function (object) {
// wait for this to resolve and after that move to next object
doSomething(object);
});
Ответы
Ответ 1
До ES2017 и async/await
(см. ниже для опции в ES2017) вы не можете использовать .forEach()
, если вы хотите дождаться обещания, потому что promises не блокируют. Javascript и promises просто не работают.
-
Вы можете связать несколько promises и сделать им инфраструктуру последовательности обещаний.
-
Вы можете выполнять итерацию вручную и продвигать итерацию только тогда, когда заканчивается предыдущее обещание.
-
Вы можете использовать библиотеку, такую как async
или Bluebird
, которая будет их упорядочивать для вас.
Существует много разных альтернатив, но .forEach()
не сделает это за вас.
Здесь пример последовательности с использованием цепочки promises с angular promises (при условии, что objects
является массивом):
objects.reduce(function(p, val) {
return p.then(function() {
return doSomething(val);
});
}, $q.when(true)).then(function(finalResult) {
// done here
}, function(err) {
// error here
});
И, используя стандартный ES6 promises, это будет:
objects.reduce(function(p, val) {
return p.then(function() {
return doSomething(val);
});
}, Promise.resolve()).then(function(finalResult) {
// done here
}, function(err) {
// error here
});
Здесь пример ручного секвенирования (предполагается, что objects
- это массив), хотя это не сообщает о завершении завершения или ошибки, подобные описанному выше:
function run(objects) {
var cntr = 0;
function next() {
if (cntr < objects.length) {
doSomething(objects[cntr++]).then(next);
}
}
next();
}
ES2017
В ES2017 функция async/wait
позволяет вам "ждать" обещания выполнить до продолжения итерации цикла при использовании не-функциональных циклов, таких как for
или while
:
async function someFunc() {
for (object of objects) {
// wait for this to resolve and after that move to next object
let result = await doSomething(object);
}
}
Код должен содержаться внутри функции async
, а затем вы можете использовать await
, чтобы заставить интерпретатора ждать выполнения обещания до продолжения цикла. Обратите внимание, что, хотя это похоже на поведение типа "блокировка", оно не блокирует цикл события. Другие события в цикле событий все еще могут обрабатываться во время await
.
Ответ 2
Да, вы можете использовать angular.forEach
для достижения этого.
Вот пример (предполагая, что objects
- массив):
// Define the initial promise
var sequence = $q.defer();
sequence.resolve();
sequence = sequence.promise;
angular.forEach(objects, function(val,key){
sequence = sequence.then(function() {
return doSomething(val);
});
});
Вот как это можно сделать, используя array.reduce
, аналогично @friend00 answer (предполагается, что objects
- массив):
objects.reduce(function(p, val) {
// The initial promise object
if(p.then === undefined) {
p.resolve();
p = p.promise;
}
return p.then(function() {
return doSomething(val);
});
}, $q.defer());
Ответ 3
проверить $q на angular:
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;
}
Ответ 4
Самый простой способ - создать функцию и вручную перебрать все объекты в массиве после каждого обещания.
var delayedFORLoop = function (array) {
var defer = $q.defer();
var loop = function (count) {
var item = array[count];
// Example of a promise to wait for
myService.DoCalculation(item).then(function (response) {
}).finally(function () {
// Resolve or continue with loop
if (count === array.length) {
defer.resolve();
} else {
loop(++count);
}
});
}
loop(0); // Start loop
return defer.promise;
}
// To use:
delayedFORLoop(array).then(function(response) {
// Do something
});
Пример также доступен на моем GitHub:
https://github.com/pietervw/Deferred-Angular-FOR-Loop-Example
Ответ 5
Это может помочь кому-то, как я попробовал несколько вышеупомянутых решений, прежде чем придумать свой собственный, который действительно работал у меня (другие не делали этого)
var sequence;
objects.forEach(function(item) {
if(sequence === undefined){
sequence = doSomethingThatReturnsAPromise(item)
}else{
sequence = sequence.then(function(){
return doSomethingThatReturnsAPromise(item)
});
}
});
Ответ 6
Это сработало для меня так. Я не знаю, подходит ли это подход, но может помочь уменьшить количество строк.
function myFun(){
var deffer = $q.defer();
angular.forEach(array,function(a,i) {
Service.method(a.id).then(function(res) {
console.log(res);
if(i == array.length-1) {
deffer.resolve(res);
}
});
});
return deffer.promise;
}
myFun().then(function(res){
//res here
});