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);
    }
};
3
  • 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 ? Commented Mar 26, 2021 at 9: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 Commented Mar 26, 2021 at 9:10
  • you may find related info in these answers, stackoverflow.com/a/37576787/3270433 Commented Mar 26, 2021 at 9:17

2 Answers 2

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 });
...
Sign up to request clarification or add additional context in comments.

Comments

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 });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.