在转到下一个对象后使 angular.forEach 等待承诺

Make angular.forEach wait for promise after going to next object

我有一个对象列表。对象被传递给延迟函数。我只想在上一个调用解决后才用下一个对象调用该函数。有什么办法可以做到这一点吗?

angular.forEach(objects, function (object) {
    // wait for this to resolve and after that move to next object
    doSomething(object);
});

检查 angular 上的 $q:

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;
}

在 ES2017 和 async/await 之前(请参阅下面的 ES2017 中的选项),如果您想等待承诺,则不能使用 .forEach(),因为承诺不会阻塞。 Javascript 并且 promises 只是行不通。

  1. 您可以链接多个承诺并使承诺基础结构对它们进行排序。

  2. 可以手动迭代,只在上一个promise完成后才推进迭代

  3. 您可以使用像 asyncBluebird 这样的库来为您排序。

有很多不同的选择,但.forEach()不会为你做。


这是一个使用 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 承诺,这将是:

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 特性确实允许您在使用非基于函数的循环(例如 forwhile:

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 告诉解释器在继续循环之前等待承诺解决。请注意,虽然这似乎是 "blocking" 类型的行为,但它并没有阻止事件循环。 await.

期间事件循环中的其他事件仍然可以处理

是的,您可以使用 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 的回答(假设 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());

最简单的方法是创建一个函数,并在每个承诺完成后手动迭代数组中的所有对象。

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

这可能对某些人有帮助,因为我尝试了上述几种解决方案,然后提出了我自己的实际对我有用的解决方案(其他的没有)

  var sequence;
  objects.forEach(function(item) {
     if(sequence === undefined){
          sequence = doSomethingThatReturnsAPromise(item)
          }else{
          sequence = sequence.then(function(){
               return doSomethingThatReturnsAPromise(item)
                     }); 
                 }
        });

它像这样对我有用。我不知道这是否是一种正确的方法,但可以帮助减少线路

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
});

我使用一个简单的解决方案来连接打印机,等待承诺结束再转到下一个。

angular.forEach(object, function(data){
    yourFunction(data)
    .then(function (){
        return;
    })
})