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:>
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);
})
});