在forEach循环中保证

在一个Node应用程序中,我需要以同步的方式迭代一些项目,但循环内的一些操作是asynchronous的。 我的代码现在看起来像这样:

someAPIpromise().then((items) => { items.forEach((item) => { Promise.all[myPromiseA(item), myPromiseB(item)]).then(() => { doSomethingSynchronouslyThatTakesAWhile(); }); } } 

当这个items是1的数组的时候,这会有奇效。但是,一旦有多个项目, promise.all()就会立刻为数组中的每个项目开始,而不用等待循环中的操作结束。

所有这一切…如何确保数组中的每个项目的整个操作同步运行(即使某些操作是asynchronous并返回一个承诺)?

非常感谢!

ñ

你正在构build一些承诺,但它们都是asynchronous的。 你构build了Promise1,Promise2,Promise3,但是一旦他们在野外,他们都在同时射击。 如果你想要同步行为,你必须将它们链接在一起,所以Promise1的.then()执行Promise2等等。 在过去我已经使用Array.reduce。

 someAPIpromise().then((items) => { items.reduce((accumulator, current) => accumulator.then(() => Promise.all[myPromiseA(item), myPromiseB(item)]).then(() => doSomethingSynchronouslyThatTakesAWhile(); ) ) , Promise.resolve()); 

如果你喜欢,你可以把它写成一个辅助函数,这可能会使事情变得更加清晰。

 function execSequentially (arr, func) { return arr.reduce( (accumulator, current) => accumulator.then(() => func(current)), Promise.resolve()); } 

该函数执行为

 execSequentially(items, item => console.log(item)); 

当然用你想做的事情replaceconsole.log。

帮手function的方法也是一个较小的侵入性的变化。 助手应用于您的原始代码:

 someAPIpromise().then((items) => { execSequentially(items, (item) => Promise.all[myPromiseA(item), myPromiseB(item)]).then(() => { doSomethingSynchronouslyThatTakesAWhile(); }); ); }); 

你应该能够删除.forEach() ; 使用Array.prototype.reduce()Promise值数组返回给Promise.all() 。 如果带有items元素是一个函数,调用函数,否则包装在Promise.resolve() ,它应该返回结果与items数组中相同的顺序

请参阅Promise.all()

Promise.all传递来自它传递的可迭代对象中所有promise的值数组。 值的数组维护原始可迭代对象的顺序,而不是承诺解决的顺序。如果迭代数组中传递的内容不是承诺,则由Promise.resolve其转换为一个。

 var arr = [1, // not asynchronous function j() { return new Promise(function(resolve) { setTimeout(function() { resolve(2) }, Math.floor(Math.random() * 10000)) }) }, // asynchronous 3, // not asynchronous function j() { return new Promise(function(resolve) { setTimeout(function() { resolve(4) }, Math.floor(Math.random() * 3500)) }) }, // asynchronous 5, // not asynchronous Promise.resolve(6), // asynchronous 7 ]; Promise.all(arr.reduce(function(p, next) { var curr = Promise.resolve(typeof next === "function" ? next() : next); return p.concat.apply(p, [curr.then(function(data) { console.log(data); return data })]); }, [])) .then(function(data) { console.log("complete", data) }) 

好吧,我们能够得到它的工作方式: array.reduce()在Promises的帮助下。 最终的结果是:

 myAsyncAPIcall.then(items => { items.reduce((current, nextItem) => { return current.then(() => { return new Promise(res => { Promise.all([myPromiseA(nextItem), myPromiseB(nextItem]).then(() => { someSynchronousCallThatTakesAWhile(nextItem); res(); }).catch(err => { console.log(err); }); }); }); }, Promise.resolve()) }) 

它的工作方式是,通过将数组中的每一项包装在它自己的Promise(resolve,reject)中 ,我们可以确保每次迭代都是同步运行的,因为一次迭代的完成将触发需要parsing下一个Promise,等等等等。 在解决的每个承诺中,可以尽可能多地asynchronous启动调用,只要知道它们将仅限于父承诺,直到完成。

我希望这可以帮助人们!

如何保持forEach …

 var stopAllProcessingOnServerLowValue= false; function someAPIpromise(){ var arr = [ {id:123, urlVal:null}, {id:456, urlVal:null}, {id:789, urlVal:null}, {id:101112, urlVal:null} ]; return new Promise(function(resolve){ setTimeout(function(){ resolve(arr) }, 3000); }) } function extractSomeValueRemotely(url){ return new Promise(function(resolve, reject){ console.log("simulate an async connection @ %s to request a value", url); setTimeout(function(){ var someRandom = Math.round(Math.random()*7) + 1; console.log("%s responded with %s", url, someRandom); if(someRandom > 4){ resolve(someRandom); } else{ var issue = "Urls result is too low ("+someRandom+" <= 4)."; console.warn(issue+".It will be set to -1"); if(stopAllProcessingOnServerLowValue){ reject(issue+".Operation rejected because one or mole server results are too low ["+someRandom+"]."); } else{ resolve(-1); } } }, 1500*Math.round(Math.random()*7) + 1); }); } function addAnotherExtraParamToItem(_item){ return new Promise(function(resolve, reject){ setTimeout(function(){ console.log("setting extra2 on %s", _item.id); _item['extra'] = "additional_processing_"+_item.id; resolve(_item); }, 1500*Math.round(Math.random()*5) + 1); }); } function addOrderIndexToItem(_item, _order){ return new Promise(function(resolve, reject){ setTimeout(function(){ console.log(">> setting order %s on %s",_order, _item.id); _item['order'] = _order; resolve(_item); }, 1500*Math.round(Math.random()*3) + 1); }); } someAPIpromise().then(function(items){ var perItemPromises = []; items.forEach(function(item, idx){ perItemPromises.push( new Promise(function(pulseItemResolve, pulseItemReject){ var itemStepsPromises = []; itemStepsPromises.push(addAnotherExtraParamToItem(item)); itemStepsPromises.push(extractSomeValueRemotely("http://someservice:777/serve-me") .catch( function(reason){ //the entire item will be rejected id pulseItemReject(reason); }) ); itemStepsPromises.push(addOrderIndexToItem(item, idx)); //promise that ensure order of execution on all previous async methods Promise.all(itemStepsPromises).then(function(values){ //0 - first is result from addAnotherExtraParamToItem var theItem = values[0]; //it returns the item itself //urlVal has not been set yet // 1 - second promise return the url result var serverResult = values[1]; //2 - third promise add the order index but we do not care to inspect it because theItem reference in value[0] has been already updated. // console.info(values[2]); //sets the url result in the item theItem.urlVal = serverResult; console.log("urlVal set to:", theItem.urlVal); //resolve the prepared item pulseItemResolve(theItem); }); }) .catch(function(reason){ //escalate error throw new Error(reason); }) ) }); Promise.all(perItemPromises).then(function(resultsInAllItems){ console.info("Final results:"); console.info(resultsInAllItems); }).catch(function(finalReject){ console.error("Critical error:",finalReject); }) }); 

经过大量研究,对我的确切答案在这里…

我已经阅读了一系列有用的纯JavaScript(没有插件) – Promise Iterator – 可以很容易地在我的项目中使用(一行)的解决scheme ,最后我发现了这个解决scheme

 function one_by_one(objects_array, iterator, callback) { var start_promise = objects_array.reduce(function (prom, object) { return prom.then(function () { return iterator(object); }); }, Promise.resolve()); // initial if(callback){ start_promise.then(callback); }else{ return start_promise; } } 

有关使用的详细信息和示例,请访问该链接 。

它也允许直接处理callback。

这是我经过许多日子与Promise迭代挣扎并且从许多问题,博客和官方网站testingMULTIPLE解决scheme之后发现的最合理和可重用的方法。

如果你还在为一个明确的答案而努力,那就试一试。