Firebase - ожидание данных от асинхронного вызова базы данных

Я пытаюсь получить некоторые данные из firebase с двумя вызовами базы данных. Моя стратегия получения учетных записей только для 1 пользователя выглядит следующим образом:

  • получить все номера учетных записей для вошедшего в систему пользователя
  • новый вызов fetch для получения массива учетных записей (со всеми их данными)
  • диспетчерский вызов с полным массивом

Моя структура firebase выглядит так.

введите здесь описание изображения

Моя проблема в том, что второй вызов выборки всегда заканчивается последним.

export const fetchAccounts = () => {
   return (dispatch, getState) => {
      const accounts = [];
      const uid = getState().auth.uid;

      return database.ref(`users/${uid}/accounts`).once('value').then((snapshot) => {
         snapshot.forEach((element) => {
            database.ref(`accounts/${element.key}`).once('value').then((snapshot) => {
               accounts.push(snapshot.val());
               console.log('snapshot: ', accounts);
            })
         });
         console.log('Acc 1:', accounts);
      }).then(() => {
         console.log('Acc 2:', accounts)
      })

      // dispatch call with full array
   }
};

введите здесь описание изображения

Я вызываю действие из основного файла

  reduxStore.dispatch(fetchAccounts()).then(()=>{
     renderApp();
  });

Можно ли дождаться завершения обоих вызовов базы данных, а затем вызвать функцию отправки с полностью заполненным массивом? Цените любую идею.


person Toesmash    schedule 17.03.2018    source источник


Ответы (1)


Ваш первый then() ничего не возвращает, поэтому второй then() сработает немедленно, что вы и видите в консоли. Однако цикл запросов является асинхронным и еще не завершен.

Создайте массив промисов и используйте Promise.all() поэтому второй then() не сработает, пока все запросы в цикле не будут выполнены.

export const fetchAccounts = () => {
   return (dispatch, getState) => {
      const accounts = [];
      const uid = getState().auth.uid;

      return database.ref(`users/${uid}/accounts`).once('value').then((snapshot) => {
         const accountPromises =[];// array to store promises

         snapshot.forEach((element) => {
            // reference promise to pass into array
            const request = database.ref(`accounts/${element.key}`).once('value').then((snapshot) => {
               accounts.push(snapshot.val());
               console.log('snapshot: ', accounts);
            });
            // push request promise to array
            accountPromises.push(request)

         });
         console.log('Acc 1:', accounts);// accounts should still be empty here since requests are in progress
         // return promise that doesn't resolve until all requests completed
         return Promise.all(accountPromises);
      }).then(() => {
         // shouldn't fire until all the above requests have completed
         console.log('Acc 2:', accounts);
         return accounts // return array to use in next `then()`
      })

      // dispatch call with full array
   }
};
person charlietfl    schedule 17.03.2018
comment
Большое спасибо! Комментарии очень помогли понять логику :) - person Toesmash; 17.03.2018
comment
Имейте в виду, что если какой-либо из этих запросов не будет выполнен, promise.all() не будет разрешено, поэтому вам нужно будет добавить дополнительные catch() для обработки ошибок. - person charlietfl; 17.03.2018