簡體   English   中英

promise.all在forEach循環中 - 一切都在觸發

[英]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.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM