使 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 基础架构对它们进行排序。
您可以手动迭代并仅在前一个承诺完成时推进迭代。
您可以使用像 async
或 Bluebird
这样的库来为您排序。
有很多不同的选择,但.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
功能确实允许您在使用基于非函数的循环(例如 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
期间仍然可以处理。
【讨论】:
您的“减少”解决方案很优雅,但效果并不理想。$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事件
如何使 display:flex 项目自动转到下一行? [复制]