0

I expect when I call an async function to resolve promise at the end, not before.

const urls = await uploadImages({ imageChanges, questions });
// ...next step
// I will use urls

But after calling await uploadImages() it continues to run until const data = await fetch(image.src);

And then ...next step starts. How can I make it wait for imageChanges.forEach loop finish ? Should I create another nested function inside ?

const uploadImages = async ({ imageChanges, questions }) => {
    if (!imageChanges.length) return null;

    const storage = firebase.storage();
    let urls; 

    try {
        //** convert each new image's src from blob to downloadUrl. */
        imageChanges.forEach(async image => {
            const questionId = questions.findIndex(q => q.id === image.questionId);
            const imagePath = `${questionId}.jpg`;
            const storageRef = storage.ref(imagePath);

            // ** 
            const data = await fetch(image.src);
            const blob = await data.blob();

            const uploadTaskSnapshot = await storageRef.put(blob);
            const downloadURL = await uploadTaskSnapshot.ref.getDownloadURL();
            urls.push(downloadURL)
        });

        return urls;
    } catch (error) {
        console.log(error.message);
    }
};
Er.Se
  • 461
  • 3
  • 11
  • I am trying to understand your question, are you saying, after you call `await uploadImages({ imageChanges, questions });`, when the loop in completed it starts the next steps and still the loop tat completed fetches the images in background ? – Sohan Mar 26 '21 at 09:02
  • I edited question. I want it return "urls" to me. But before "const data = await fetch(image.src);" code line, my caller function's next line starts to run without waiting urls. And some time later it goes back to "uploadImages" function – Er.Se Mar 26 '21 at 09:10
  • you may find related info in these answers, https://stackoverflow.com/a/37576787/3270433 – FerdousTheWebCoder Mar 26 '21 at 09:17

2 Answers2

1

forEach with async doesn't work as expected. Read this answer for more info.

Try like this

const uploadImages = async ({ imageChanges, questions }) => {
  if (!imageChanges.length) return null;

  const storage = firebase.storage();

  try {
    const imageChangesUrlPromise = imageChanges.map(async () => {
      const questionId = questions.findIndex(q => q.id === image.questionId);
      const imagePath = `${questionId}.jpg`;
      const storageRef = storage.ref(imagePath);
      const data = await fetch(image.src);
      const blob = await data.blob();
      const uploadTaskSnapshot = await storageRef.put(blob);
      const downloadURL = await uploadTaskSnapshot.ref.getDownloadURL();
      return downloadURL;
    })

    return await Promise.all(imageChangesUrlPromise);
  } catch (error) {
    console.log(error.message);
  }
};

and then

const urls = await uploadImages({ imageChanges, questions });
...
Naren
  • 4,152
  • 3
  • 17
  • 28
0

JavaScript does this because forEach is not promise-aware. It cannot support async and await. You cannot use await in forEach.

If you use await in a map, map will always return an array of promises. This is because asynchronous functions always return promises.

By littile modification to your code, this should work,

const uploadImages = async ({ imageChanges, questions }) => {
if (!imageChanges.length) return null;

const storage = firebase.storage();
let urls;

try {
    //** convert each new image's src from blob to downloadUrl. */
    await Promise.all(imageChanges.map(async image => {
        const questionId = questions.findIndex(q => q.id === image.questionId);
        const imagePath = `${questionId}.jpg`;
        const storageRef = storage.ref(imagePath);

        // **
        const data = await fetch(image.src);
        const blob = await data.blob();

        const uploadTaskSnapshot = await storageRef.put(blob);
        const downloadURL = await uploadTaskSnapshot.ref.getDownloadURL();
        urls.push(downloadURL)
    }));

    return urls;
 } catch (error) {
    console.log(error.message);
 }
};

const urls = await uploadImages({ imageChanges, questions });
georg
  • 211,518
  • 52
  • 313
  • 390
Sohan
  • 6,252
  • 5
  • 35
  • 56