Обещания оцениваются перед Promise.all во вложенном forEach, что приводит к пустому Promise.all - PullRequest
0 голосов
/ 10 октября 2019

У меня необычное поведение.

В основном, как часть моего кода, у меня есть функция, которая использует вложенные циклы для построения обещания и добавления его в список обещаний.

После того, как вложенные циклы завершены, я хотел бы оценить список обещаний с помощью promise.all ().

В прошлом я успешно справлялся с одним циклом forEach. Вложенность, кажется, вызывает некоторые проблемы, а именно тестирование показывает, что Promise.all вызывается до завершения вложенного цикла forEach, в результате чегов нем вызывается пустой список и, следовательно, возвращается пустой список.

У меня такое ощущение, что проблема в том, что мне не хватает инструкции return где-то в моем вложенном цикле 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: Я все еще начинаю с обещаниями и TDD, я более чем рад услышать отзывы о любом запахе кода.

Ответы [ 3 ]

1 голос
/ 10 октября 2019

Да, поэтому проблема, которую я вижу, состоит в том, что ваш цикл для каждого вызывает асинхронный код и ожидает, что он будет выполняться синхронно.

Я бы, вероятно, сделал что-то вроде ...

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)
  })
0 голосов
/ 10 октября 2019

Вы выполняете 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
}
0 голосов
/ 10 октября 2019

Вам нужно вложить ваше обещание разрешения. Как то так:

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  
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...