[英]promise.all inside a forEach loop — everything firing at once
在Node應用程序中,我需要以同步方式迭代某些項,但循環內的一些操作是異步的。 我的代碼現在看起來像這樣:
someAPIpromise().then((items) => {
items.forEach((item) => {
Promise.all[myPromiseA(item), myPromiseB(item)]).then(() => {
doSomethingSynchronouslyThatTakesAWhile();
});
}
}
當items
是1的數組時,這會產生奇跡。但是,一旦有多個項目, promise.all()
將立即為數組中的每個項目啟動,而無需等待循環中的操作結束。
所有這些......我怎樣才能確保數組中每個項的整個操作是同步運行的(即使某些操作是異步並返回一個promise)?
非常感謝!
ñ
你正在構建幾個promise,但它們都是異步的。 你構建了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));
當然用你想要的東西取代console.log。
輔助函數方法對變更的侵入性也較小。 幫助程序應用於您的原始代碼:
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的值數組。 值數組維護原始可迭代對象的順序,而不是promises被解析的順序。如果在可迭代數組中傳遞的東西不是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.prototype.shift()
, Promise.resolve()
.then()
,遞歸
function re(items, res) {
if (items.length) {
var curr = items.shift();
return Promise.resolve(
typeof curr === "function"
? curr()
: curr
).then(function(data) {
// values from `arr` elements should be logged in sequential order
console.log(data);
res.push(data)
}).then(re.bind(null, items, res))
} else {
return ["complete", res]
}
}
var _items = arr.slice(0);
re(_items, [])
.then(function(complete) {
console.log(complete)
})
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 ]; function re(items, res) { if (items.length) { var curr = items.shift(); return Promise.resolve( typeof curr === "function" ? curr() : curr ).then(function(data) { // values from `arr` elements should be logged in sequential order console.log(data); res.push(data) }).then(re.bind(null, items, res)) } else { return ["complete", res] } } var _items = arr.slice(0); re(_items, []) .then(function(complete) { console.log(complete) })
好吧......我們能夠讓它工作的方式:在Promises的幫助下, array.reduce() 。 最終結果:
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中(解析,拒絕) ,我們可以確保每次迭代都是同步運行的,因為一次迭代的完成將觸發解決下一個Promise的需要,並且等等等等。 在每個promise解析中,調用可以根據需要異步啟動,並且知道它們只會作用於父承諾,直到完成為止。
我希望這對人們有所幫助!
如何保持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 - 可以在我的項目中輕松使用(一行),最后我找到了Salketer的解決方案 :
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;
}
}
有關詳細信息和使用示例,請訪問該鏈接 。
它還允許直接處理回調。
這是我在經過多年努力進行Promise迭代並從許多問題,博客和官方網站測試MULTIPLE解決方案后發現的最合乎邏輯且可重復使用的方法。
如果你也在爭取一個明確的答案,試一試。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.