Функция Promise.all разрешается до завершения обещания с добавлением данных в массивы - PullRequest
0 голосов
/ 18 февраля 2019

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

Я пытался вести журнал консоли каждый раз, когда doc.data () помещался в exportArray, и заметил, что он регистрирует это после вывода массива.Так, например ...

Ожидаемый вывод

doc.data() // For Each doc    
Array[] // Filled with data and length 54
Length: 54

Фактический вывод

Array[] // Filled with data and length 54
Length: 0
doc.data() // For Each doc

let exportArray = [];
let promises = [];

db.collection('lists').doc('List 1').collection("members")
    .get().then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
        promises.push(
            new Promise(function (resolve, reject) {
                exportArray.push(doc.data());
                resolve();
                console.log('before');
            })
        );
    });
});

Promise.all(promises).then(function () {
    console.log(exportArray); // Logs correctly with all data with length 54
    console.log(exportArray.length); // Logs as 0 for some reason


});

В идеале это должно вывести exportArray с его данными ANDдлина 54. Однако он выводит данные, но длина выводится как 0. (и да, я нажал на массив данных в консоли, и он показывает длину 54)

Почему массив заполняетсяно я не могу правильно использовать методы, например exportArray.length?

Ответы [ 3 ]

0 голосов
/ 18 февраля 2019

Поскольку db.collection('lists').doc('List 1').collection("members").get() немедленно возвращается с обещанием, которое разрешается только после завершения запроса, ваш код продолжит выполнение Promise.all() для пустого списка и также сразу же вернется, потому что ждать нечего.Через некоторое время ваши снимки будут готовы и promises будет заполнено.

Вы должны вызывать Promises.all () только после заполнения всего массива.

0 голосов
/ 18 февраля 2019

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

db.collection('lists')
  .doc('List 1')
  .collection("members")
  .get()
  .then(querySnapshot => querySnapshot.map(doc => doc.data())
  .then(promises => Promise.all(promises))
  .then(exportArray => {
    console.log(exportArray); // Logs correctly with all data with length 54
    console.log(exportArray.length); // Logs as 0 for some reason
  })

Об антипаттерне:

  • избегайте использования new Promise() и подобных.это редко необходимо, обычно у вас уже есть канал Promise, из которого вы можете получить.

  • Обещания - это обертывающие значения, которых у вас еще нет.Не пытайтесь «развернуть» эти значения, выполнив что-то вроде somePromise.then(value => { externalVariable = value; }) или в вашем случае это массив.
    Теперь у вас есть переменная, которая будет (в какой-то момент в будущем) содержать желаемое значение, но в данный момент она пуста / недействительна.Итак, теперь вам нужно реализовать собственное управление состоянием, чтобы проверить, когда значение стало действительным;в основном дублирует большую часть логики Обещания;)

0 голосов
/ 18 февраля 2019

Вы должны позвонить Promise.all на promises после того, как было заполнено обещаниями, что происходит асинхронно при обратном вызове then.Теперь вы выполняете его синхронно, когда ничего этого еще не произошло.

Так же:

db.collection('lists').doc('List 1').collection("members").get().then(function(querySnapshot) {
    let promises = querySnapshot.docs.map(function(doc) { //  <-- use docs.map
        return doc.data(); // <-- just return `data()`. No need for a new promise
    });
    // Must be here:
    return Promise.all(promises).then(function (exportArray) { // <--- data arg!
        console.log(exportArray);
        console.log(exportArray.length);
    });
});

Примечания:

  • Нет необходимости в new Promiseкогда у вас есть значение для разрешения с готовностью.
  • Вместо forEach получите массив из моментального снимка запроса с помощью .docs и встроенной JS .map().
  • То, что вы видите массив в консоли, но с длиной 0, является поведением консоли: она только регистрирует ссылку на массив, но затем, когда вы раскрываете его в консоли, он имеет всреднее время было заселено;так что вы видите данные.Но этого не было в момент регистрации, о чем length: 0 говорит вам.

Упрощение

Согласно документации по пожарной базе, doc.data() возвращает данные, а не обещание, поэтому нет смысла использовать Promise.all, достаточно простого map:

db.collection('lists').doc('List 1').collection("members").get().then(function(querySnapshot) {
    return querySnapshot.docs.map(function(doc) {
        return doc.data();
    });
}).then(function (exportArray) {
    console.log(exportArray);
    console.log(exportArray.length);
});
...