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

Posted

技术标签:

【中文标题】使 angular.forEach 在转到下一个对象后等待承诺【英文标题】:Make angular.forEach wait for promise after going to next object 【发布时间】:2015-05-13 01:17:50 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

在 ES2017 和 async/await 之前(请参阅下面的 ES2017 选项),如果你想等待一个 Promise,你不能使用 .forEach(),因为 Promise 不会阻塞。 javascript 和 Promise 不能那样工作。

    您可以链接多个 Promise,并使 Promise 基础架构对它们进行排序。

    您可以手动迭代并仅在前一个承诺完成时推进迭代。

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

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


这是一个使用带有角度承诺的承诺链进行排序的示例(假设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 告诉解释器在继续循环之前等待承诺解决。请注意,虽然这似乎是“阻塞”类型的行为,但它并没有阻塞事件循环。事件循环中的其他事件在await期间仍然可以处理。

【讨论】:

您的“减少”解决方案很优雅,但效果并不理想。 $q.defer().resolve() 解析了一个承诺,但返回未定义,所以 p 对于第一个元素是未定义的。 @AgDude - 如果您知道建议什么,请对我的回答提出修改建议。我没有跟上所有不同的专有 Promise 库(当现在有一个 ES6 标准时)所以如果这不是创建已解决的 Promise ion angularjs 来启动链的正确方法,那么我不是确定它应该是什么.. 优雅干净的解决方案 初始值 $q.dever().resolve() 对我不起作用(Angular 1.4.3),p 在第一次调用时是 undefined。但这确实有效:$q(function(resolve) resolve();) @Artjom - 在 Angular 1.5.1 中,the doc 表示 .resolve() 返回一个承诺。也许这是在 1.4.3 之后添加的?无论如何,您的方法也可以正常工作。此外,它必须是 $q.defer().resolve() - 您在评论中拼写不同。我想你也可以使用$q.when(true)【参考方案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 的回答(假设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());

【讨论】:

您说的很对!这应该是公认的答案。您可以使用 angular.forEach 来实现这一点。【参考方案3】:

检查 $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;

【讨论】:

这在我看来就像它并行运行所有操作并使用q.all() 知道它们何时完成。我虽然 OP 询问如何一次对操作进行排序。【参考方案4】:

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

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】:

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

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

【讨论】:

【参考方案6】:

这可能对某人有所帮助,因为我尝试了上述几种解决方案,然后才提出了对我真正有用的解决方案(其他解决方案没有)

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

【讨论】:

【参考方案7】:

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

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 在转到下一个对象后等待承诺的主要内容,如果未能解决你的问题,请参考以下文章

确保在转到下一页之前发送了Google Analytics事件

在转到下一个视图控制器(Swift)之前进行异步调用

Nextjs如何在转到下一页时不卸载上一页(保持状态)

如何使 display:flex 项目自动转到下一行? [复制]

如何在转到角度之前将 SKSpriteNode 旋转 360°

如何使 segue 等待功能在按钮按下时完成?