2hTu2
2hTu2

Reputation: 398

Promise not waiting in async Object entries firebase call

I have a main function that awaits and connects a few functions in parallel like this:

Main function:

console.log('A');
const pivotData = await Promise.all(
    Object.entries(thenWidgets.val()).map(widget => {
      getPivot(widget, userId)
      console.log('B');
    });
);
console.log('C');
console.log('D');
const mainData = await Promise.all(
    pivotData.map(widget => getMain(widget))
);
console.log('F');
mainData.map(item => {
    return (finalObject[item.widgetId] = item);
});
console.log('G');

The works fine until it hits mainData below console.log('D'). It seems to not await the promise and skip right to console.log('F') without waiting for the getMain function which hold console.log('E')

getMain() Function:

const getMain = widget => {
  return new Promise(resolve => {
    var requests = [];
      const pivotData = {};
        Object.keys(widget.pivot).map(item => {
          const mainRef = firebase
            .database()
            .ref()
            .child(widget['default'].type)
            .child(item);

          mainRef.once('value', snapMain => {
            pivotData[item] = snapMain.val();
          }).then(() => {
            widget['main'] = pivotData;
            console.log('E');
            requests.push('test');
          });
          console.log('E2')
        })
        Promise.all(requests)
        .then(() => {
          console.log('E3');
          resolve(widget);
        })
        .catch(error => console.log(`Error in promises ${error}`));
  });
};

The expected outcome here is: 'E, E2, E, E2, E3' but what i get is 'E2, E2, E3' and then it returns the promise. Later, somewhere after all the promises have resolved i get 'E, E'. So the expected outcome for the whole thing should be 'A, B, C, D, E, E2, E, E2, E3, F, G' what I have now is 'A, B, C, D, E2, E2, E3, F, G, E, E'

It seems that the promise inside the Object.keys inside getMain() is not waiting for the mainRef.once. Perhaps I am missing something.

My question here is: What is going wrong that the promise is not waiting for the getMain() function?

Upvotes: 1

Views: 325

Answers (1)

Alex Stepanov
Alex Stepanov

Reputation: 411

Let's take a close look at the getMain function, specifically the lines around console.log('E'). What is happening here is the following:

  1. Here you make a call to Firebase, .once(...) returns a promise that is not await-ed so it gets resolved later, after the getMain has returned
  2. Since the promise is not await-ed, the next line to execute is console.log('E2') and then it goes on the next iteration of the loop
  3. After the loop finishes, requests will be an empty array (because the values were expected to come from the .once(...) handlers but we never awaited on those promises). Empty array gets resolved immediately, so the Promise.all(requests) falls through to then portion, logs out 'E3' and resolves the main promise, effectively returning the original widget
  4. Once getMain returns, the promises that we created earlier will start resolving and modifying the widget "behind the scenes" so the next time you try to do anything with the widget, you may find something completely unexpected.

In order to fix this function, you need to await on the promises returned by Firebase before proceeding to the main promise at the bottom - you could do it either by putting the results of .once() calls into a new array and Promise.all(...) on that array, or by composing a "chained" promise on each iteration of the loop and then awaiting on it right after the loop

Hope that helps!

Upvotes: 1

Related Questions