Home > database >  How to wait for getDownloadURL to finish before proceeding?
How to wait for getDownloadURL to finish before proceeding?

Time:11-17

I'm failing to append the URL returned by getDownloadURL on the imageLinks state. I want to continue using the imageLinks state containing all the URLs but it seems the code continues running and doesn't wait for it to return the URL. How can I wait for it to return the links and then append it to the state?

const [imageLinks, setImageLinks] = useState([]);

for (let i = 0; i < 5; i  ) {
  if (images[i] !== null) {
    const fileName = new Date().getTime()   images[i].name;
    const storage = getStorage(app);
    const storageRef = ref(storage, fileName);
    const uploadTask = uploadBytesResumable(storageRef, images[i]);

    uploadTask.on(
      "state_changed",
      (snapshot) => {
        const progress =
          (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        switch (snapshot.state) {
          case "paused":
            console.log("Upload is paused");
            break;
          case "running":
            console.log("Upload is in progress");
            break;
          default:
        }
      },
      (error) => {
        alert("Images failed to upload. "   error.message);
      },
      () => {
        getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
          setImageLinks((imageLinks) => [...imageLinks, downloadURL]);
        });
      }
    );
  }
}

console.log(imageLinks); //returns empty array

CodePudding user response:

From a quick scan of the UploadTask class that is returned by uploadBytesResumable, it implements all requirement methods of a Promise. This means that you can treat it as a promise of the uploading completing, rather than implementing its status callback.

This in turn then allows you to use Promise.all to wait for all uploads to complete and all download URLs to be gotten.

let promises = [];
for (let i = 0; i < 5; i  ) {
  if (images[i] !== null) {
    const fileName = new Date().getTime()   images[i].name;
    const storage = getStorage(app);
    const storageRef = ref(storage, fileName);
    const uploadTask = uploadBytesResumable(storageRef, images[i]);
    const urlTask = uploadTask.then(() => storageRef.getDownloadURL())

    promises.push(urlTask);
  }
}

Promise.all(promises).then((downloadUrls) => 
  console.log(downloadUrls);
  setImageLinks(downloadUrls);
});

If you're in an environment/context that supports async/await, you can also use await instead of then. In that case you can modify the above, but you can even forego Promise.all and do:

const storage = getStorage(app);
let urls = [];
for (let i = 0; i < 5; i  ) {
  if (images[i] !== null) {
    const fileName = new Date().getTime()   images[i].name;
    const storageRef = ref(storage, fileName);
    await uploadBytesResumable(storageRef, images[i]);
    const url = await storageRef.getDownloadURL();

    urls.push(url);
  }
}

console.log(downloadUrls);
setImageLinks(downloadUrls);
  • Related