Wait for a Javascript function to finish executing and fetch the response from it

Viewed 77

I've a array of images and I am uploading these images to firebase storage.

      data = {
        ...data,
        downloadedUrl: [],
      };
    
      if (data?.image?.length) {
        for (const image of data?.image) {
          await uploadFile(image, data);
        }
      }

uploadFile handles the logic for uploading the image to firebase.

  const uploadFile = useCallback((file, data) => {
    if (!file) return;

    const storageRef = ref(storage, `/images/${file.name}`);
    const uploadTask = uploadBytesResumable(storageRef, file);

    uploadTask.on(
      "state_changed",
      (snap_shot) => {},
      (err) => console.log(err),
      async () => {
        await getDownloadURL(uploadTask.snapshot.ref).then((url) => {
          data.downloadedUrl.push(url);
        });
      }
    );
  }, []);

It takes few seconds to get the downloadedUrl from uploadTask and I want to store this downloadedUrl in firebase firestore when I get all the urls.

Issue with the current approach is that before getting the urls, the other function start executing and I am not able to upload this data on firestore with the downloadedUrl Here's the full function when someone clicks on form submit

const handleFormSubmit = useCallback(
    async (data) => {
      setLoading(true);
      data = {
        ...data,
        downloadedUrl: [],
      };

      if (data?.image?.length) {
        for (const image of data?.image) {
          await uploadFile(image, data);
        }
      }

      if (data.downloadedUrl.length) {
        uploadDataToFirestore(data);
      }
 
      if (!data.downloadedUrl?.length) {
        dispatch(handleAlertState({ message: "Error Occured!!!" }));
        router.push("/services");
        return;
      }
      setLoading(false);
      router.push("/");
    },
    [dispatch, router, uploadDataToFirestore, uploadFile]
  );

  const uploadDataToFirestore = useCallback(
    async (data) => {

      await setDoc(doc(db, "form-responses"), data)
        .then((response) => {
          console.log("response", response);
          dispatch(
            handleAlertState({
              message: "Success. Your request has been sent. Thank You.",
            })
          );
        })
        .catch((error) => {
          console.log("error", error);
        });
    },
    [dispatch]
  );

This bellow block of code executes the code while images are being uploaded to the cloud storage. I want to wait for the downloadedUrl and then upload the urls to firebase firestore.

      if (!data.downloadedUrl?.length) {
        dispatch(handleAlertState({ message: "Error Occured!!!" }));
        router.push("/services");
        return;
      }
1 Answers
  1. Create array of promises
  2. Use Promise.all to watch for every promise

const uploadFile = useCallback((file, data) => {
  return new Promise((resolve, reject) => {
    if (!file) reject();

    const storageRef = ref(storage, `/images/${file.name}`);
    const uploadTask = uploadBytesResumable(storageRef, file);

    uploadTask.on(
      'state_changed',
      snap_shot => {},
      err => reject(err),
      () => resolve(getDownloadURL(uploadTask.snapshot.ref)),
    );

  });
}, []);

let allPromises = [];
if (data?.image?.length) {
  for (const image of data?.image) {
    allPromises.push(uploadFile(image, data));
  }
}

let uploadedUrls = await Promise.all(allPromises);
console.log(uploadedUrls);


await Promise.all take an array of promises, we created a new array which holds the promise returned from uploadFile function. when all promises resolved then promise.all resolved as well and return array of urls. we await for Promise.all so it will not execute next line until resolved

Related