在Node应用程序中,我需要以同步方式迭代某些项目,但循环内部的一些操作是异步的。我的代码现在看起来像这样:
someAPIpromise().then((items) => {
items.forEach((item) => {
Promise.all[myPromiseA(item), myPromiseB(item)]).then(() => {
doSomethingSynchronouslyThatTakesAWhile();
});
}
}
当items
是1的数组时,这会产生奇迹。但是,一旦有多个项目,promise.all()
将立即为阵列中的每个项目启动,而无需等待操作在循环结束。
所有这些......我怎样才能确保数组中每个项目的整个操作是同步运行的(即使某些操作是异步并返回一个promise)?
非常感谢!
<磷>氮答案 0 :(得分:4)
您正在构建多个承诺,但它们都是异步的。你构建了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();
});
);
});
答案 1 :(得分:1)
您应该可以删除.forEach()
;使用Array.prototype.reduce()
将Promise
值数组返回Promise.all()
。如果带有items
的元素是一个函数,则调用函数,否则换行Promise.resolve()
,这应该返回与items
数组中的结果相同的结果
Promise.all
传递来自所有承诺的值数组 它被传递的可迭代对象。值数组维持着 原始可迭代对象的顺序,而不是承诺的顺序 解决了。如果迭代数组中传递的东西不是 保证,它由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)
})
答案 2 :(得分:1)
好吧......我们能够让它发挥作用的方式: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)中,我们可以确保每次迭代都是同步运行的,因为一次迭代的完成将触发解决下一个Promise的需要, 等等等等。在每个promise解析中,调用可以根据需要异步启动,并且知道它们只会作用于父承诺,直到完成为止。
我希望这对人们有所帮助!
答案 3 :(得分:0)
保持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);
})
});
答案 4 :(得分:0)
经过大量研究,对我来说最明确的答案就在这里......
我已经阅读了一系列解决方案,以获得有用的纯JavaScript(无插件) - Promise Iterator - 可以在我的项目中轻松使用(一行),最后我还是Salketer找到了 this solution :
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;
}
}
有关使用的详细信息和示例,请访问the link。
它还允许直接处理回调。
这是我在经过多年努力进行Promise迭代并从许多问题,博客和官方网站测试MULTIPLE解决方案后找到的最合乎逻辑且可重复使用的方法。
如果您还在努力争取明确的答案,请尝试一下。