1

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?

2hTu2
  • 378
  • 4
  • 19
  • please update your code as there are syntax errors and it's hard to tell what is wrong – Krzysztof Krzeszewski Dec 06 '19 at 11:59
  • Where do you get syntax errors? – 2hTu2 Dec 06 '19 at 12:00
  • third line of the main function, there are no braces, i don't know if you wanted to add braces or somehow return getPivot when still including the console.log, your map callback will return `SyntaxError: missing ) after argument list` – Krzysztof Krzeszewski Dec 06 '19 at 12:01
  • @KrzysztofKrzeszewski I added the braces – 2hTu2 Dec 06 '19 at 12:05
  • all right it seems you've added braces, however the `map` callback does not have a return value (returns undefined) so it will resolve immediately with no value in your promise.all statement, right now no matter what your pivotData will be an empty array (or rather array of length `Object.entries(thenWidgets.val()).length` full of undefineds) – Krzysztof Krzeszewski Dec 06 '19 at 12:06
  • Avoid using `new Promise`. It's not required here, and it's complicating your code. It's really hard to tell what `getMain` is supposed to be doing. If you have the ability to use async/await, a lot of this code can be cleaned up and easier to reason about. – Doug Stevenson Dec 06 '19 at 16:01
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Dec 08 '19 at 19:13

1 Answers1

1

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!

Alex Stepanov
  • 391
  • 1
  • 5