promise.all inside a forEach loop — everything firing at once

前端 未结 5 1955
暗喜
暗喜 2020-12-03 12:38

In a Node app, I need to iterate through some items in a synchronous fashion, but some of the operations inside the loop are asynchronous. My code right now looks like so:

5条回答
  •  南方客
    南方客 (楼主)
    2020-12-03 13:26

    How about keeping the 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);
        })
    
    
    });

提交回复
热议问题