在嵌套forEach中在Promise.all之前评估的Promise,导致Promise.all为空

时间:2019-10-10 20:06:38

标签: javascript node.js asynchronous promise jestjs

我有一些不寻常的行为。

基本上,作为我的代码的一部分,我有一个函数,该函数利用嵌套的for循环来构建承诺并将其添加到承诺列表中。

嵌套循环完成后,我想使用promise.all()评估诺言列表。

过去我已经成功地通过单个forEach循环成功做到了这一点,嵌套似乎引起了一些问题,即测试显示Promise.all在嵌套的forEach循环终止之前被调用,从而导致在一个空列表上调用,因此返回一个空列表。

我有个问题,就是我在this answer中提到的嵌套forEach循环中的某个地方缺少返回语句,但我无法确定位置。

culprit.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  
}
culprit.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:我仍然从Promise和TDD入手,很高兴听到关于任何代码气味的反馈。

3 个答案:

答案 0 :(得分:1)

是的,所以我看到的问题是您的每个循环都在调用异步代码并期望它同步执行。

我可能会做类似的事情...

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

答案 1 :(得分:0)

您要在阵列填充之前(异步发生)执行Promise.all

处理嵌套的Promise看起来似乎很困难,但是只需将Promise.all应用于内部的Promise数组,然后在外部级别将Promise.all应用于内部的所有Promise。 / p>

那您还没有准备好,因为现在您有一个可以解析为数组数组的诺言(对应于最初嵌套的诺言),因此您需要使用全新的.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
}

答案 2 :(得分:-2)

您需要嵌套您的承诺解决方案。像这样:

const otherModule = require("blablabla")
const otherOtherModule = require("blablabla2")

function nestedFunction(list){
  var promises =
  list.map(element => {
      return otherModule.getSublist(element).then(sublist => {
          return Promise.all(
            sublist.map(subelement => {
              return otherOtherModule.promiseResolvingFunction(subelement);
            })
         );
      });
  });
  return Promise.all(promises);
}

module.exports = {
  nestedFunction : nestedFunction  
}