Javascript 在嵌套的forEach中,在Promise.all之前对Promise进行求值,导致Promise.all为空

Javascript 在嵌套的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

我有一些不寻常的行为

基本上,作为我代码的一部分,我有一个函数,它利用嵌套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对扁平化的评论。