Javascript 在嵌套的forEach中,在Promise.all之前对Promise进行求值,导致Promise.all为空
我有一些不寻常的行为 基本上,作为我代码的一部分,我有一个函数,它利用嵌套for循环来构建承诺并将其添加到承诺列表中 嵌套循环完成后,我想使用promise.all()计算承诺列表 我已经成功地用一个forEach循环实现了这一点。在过去,嵌套似乎会引起一些问题,即,测试显示在嵌套forEach循环终止之前调用了Promise.all,导致在空列表上调用它,从而返回空列表 我有一种感觉,问题是我在中提到的嵌套forEach循环中的某个地方丢失了return语句,但我无法确定在哪里 罪魁祸首.jsJavascript 在嵌套的forEach中,在Promise.all之前对Promise进行求值,导致Promise.all为空,javascript,node.js,asynchronous,promise,jestjs,Javascript,Node.js,Asynchronous,Promise,Jestjs,我有一些不寻常的行为 基本上,作为我代码的一部分,我有一个函数,它利用嵌套for循环来构建承诺并将其添加到承诺列表中 嵌套循环完成后,我想使用promise.all()计算承诺列表 我已经成功地用一个forEach循环实现了这一点。在过去,嵌套似乎会引起一些问题,即,测试显示在嵌套forEach循环终止之前调用了Promise.all,导致在空列表上调用它,从而返回空列表 我有一种感觉,问题是我在中提到的嵌套forEach循环中的某个地方丢失了return语句,但我无法确定在哪里 罪魁祸首.js
const otherModule = require("blablabla")
const otherOtherModule = require("blablabla2")
function nestedFunction(list){
var promises = [];
list.forEach(element => {
otherModule.getSublist(element).then(sublist => {
sublist.forEach(subelement => {
promises.push(otherOtherModule.promiseResolvingFunction(subelement));
});
});
});
return Promise.all(promises);
}
module.exports = {
nestedFunction : nestedFunction
}
罪魁祸首.test.js
const culprit = require("culpritpath")
// for mocking
const otherModule = require("blablabla")
otherModule.getSublist = jest.fn(() => Promise.resolve([{}, {}, {}]))
const otherOtherModule = require("blablabla2")
otherOtherModule.promiseResolvingFunction = jest.fn(() => Promise.resolve())
describe("nestedFunction()", ()=>{
it("returns an array of resolved promises", () => {
return culprit.nestedFunction([{}, {}]).then(res => {
expect(res).toHaveLength(6);
})
})
})
相反,我得到的是res
是[]
。进一步的测试表明,promiseResolvingFunction
被调用的次数是正确的,据我所知,Promise.all
是在嵌套的forEach循环完成之前被调用的
PS:我仍在开始使用promises和TDD,我非常高兴听到关于任何代码气味的反馈。您需要嵌套您的promise解决方案。大概是这样的:
const otherModule=require(“blablabla”)
const othermodule=require(“bla2”)
函数嵌套函数(列表){
风险值承诺=
list.map(元素=>{
返回otherModule.getSublist(元素)。然后(sublist=>{
回报你的承诺(
sublist.map(子元素=>{
返回OtherModule.PromiseSolvingFunction(子元素);
})
);
});
});
返回承诺。全部(承诺);
}
module.exports={
nestedFunction:nestedFunction
}
是的,所以我看到的问题是,for-each循环调用异步代码,并期望它同步执行
我可能会做一些像
var promises = list.map(element => {
return otherModule.getSublist(element).then(sublist => {
// Map all of the sublists into a promise
return Promise.all(sublist.map(subelement => {
return otherOtherModule.promiseResolvingFunction(subelement));
}));
});
});
return Promise.all(promises);
当然,最后你会得到一个数组。如果您希望将结果保留为子列表项的平面数组,另一个选项是首先获取所有列表,然后从这些结果中获取所有子列表
return Promise.all(list.map( element => otherModule.getSublist(element)))
.then((sublists) => {
let subListPromises = [];
// Loop through each sublist, turn each item in it into a promise
sublists.forEach( sublist => {
sublistPromises = [
...sublistPromises,
sublist.map( subelement => otherOtherModule.promiseResolvingFunction(subelement))
]
})
// Return a promise dependent on *all* of the sublist elements
return Promise.all(sublistPromises)
})
在填充数组(异步发生)之前执行
Promise.all
处理嵌套承诺可能看起来很困难,但只需将Promise.all
应用于承诺的内部数组,然后在外部级别,将Promise.all
应用于来自内部级别的所有承诺
那么您还没有准备好,因为现在您有了一个解析为数组数组的承诺(对应于最初嵌套的承诺),所以您需要使用全新的.flat
方法或[]方法将其展平。concat
:
function nestedFunction(list) {
// Get promise for the array of arrays of sub values
return Promise.all(list.map(element => {
return otherModule.getSublist(element).then(sublist => {
// Get promise for the array of sub values
return Promise.all(sublist.map(subelement => {
return otherOtherModule.promiseResolvingFunction(subelement);
}));
});
})).then(matrix => [].concat(...matrix)); // flatten the 2D array
}
问题是您的
.push()
调用嵌套在另一个promise解析中。因此,您的外部.forEach()
在任何.then()
执行之前完成,并且当您将其传递给Promise.all()
时,承诺数组仍然为空,确切地说,Promise.all(承诺)正在外部调用code>。然后
。所以在之前。然后
甚至执行您已经调用的承诺。所有
大家好,谢谢,我选择了一个解决方案。请问为什么所有的解决方案都选择了map
而不是forEach
?您好,谢谢您的回答。恐怕这样做会使承诺未定义。我通过在最终返回语句之前添加一个console.log(promises)进行检查。这是不是因为我过于简单的嘲笑?顺便说一句,有人来否决了所有的问题/评论,不是我。谢谢。这就是我在手机上写代码的收获。编辑后在第一张地图中添加缺少的退货非常感谢,这很有效!同样感谢@trincot对扁平化的评论。