How to wait for Firebase storage image upload, then run next function

Viewed 1106

I'm trying to write a function that uploads several images to Firebase, saves the URLs that get returned to an object, and then uploads that object to my Cloud Firestore. I don't really have a firm understanding of async/await or promises, so if anyone could help, it would be much appreciated.

Basically, I want uploadImages() to finish running and then run uploadData(), where saveIssue() is fired when a form is submitted.

Here's what I'm working with:

saveIssue() {
  this.uploadImages();
  this.uploadData();
},
uploadData() {
  let self = this;
  db.collection("issues")
    .add(self.issue)
    .then(docRef => {
      self.$router.push({
        name: "ReportPage",
        params: { issueId: docRef.id }
      });
    })
    .catch(error => {
      console.error(error);
    });
},
uploadImages() {
  const storageRef = storage.ref();
  let self = this;
  this.imagePreviews.forEach(image => {
    let imageName = uuidv1();
    let fileExt = image.fileName.split(".").pop();
    let uploadTask = storageRef
      .child(`images/${imageName}.${fileExt}`)
      .putString(image.base64String, "data_url");
    uploadTask.on("state_changed", {
      error: error => {
        console.error(error);
      },
      complete: () => {
        uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
          self.issue.images.push(downloadURL);
        });
      }
    });
  });
},
2 Answers

You have to use promises, as explained here. You can rewrite your code to support async wait, since firebase supports it out of the box, but for the beginning it can be like this:

async saveIssue() {
    await this.uploadImages();
    await this.uploadData();
  },
  uploadData() {
    return new Promise((resolve, reject) => {
      let self = this;
      db.collection("issues")
        .add(self.issue)
        .then(docRef => {
          self.$router.push({
            name: "ReportPage",
            params: {
              issueId: docRef.id
            }
          });
          resolve();
        })
        .catch(error => {
          console.error(error);
          reject(error);
        });
    })
  },
  uploadImages() {
    return new Promise((resolve, reject) => {
      const storageRef = storage.ref();
      let self = this;
      this.imagePreviews.forEach(image => {
        let imageName = uuidv1();
        let fileExt = image.fileName.split(".").pop();
        let uploadTask = storageRef
          .child(`images/${imageName}.${fileExt}`)
          .putString(image.base64String, "data_url");
        uploadTask.on("state_changed", {
          error: error => {
            console.error(error);
            reject(error);
          },
          complete: () => {
            uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
              self.issue.images.push(downloadURL);
              resolve();
            });
          }
        });
      });
    })
  },

Okay, I found a solution based on @AlexBrohshtut's answer. I still had an issue where the first loop finishing would allow the await to continue, the code below seems to fix that. Happy to update if anyone has a more succinct answer! (I'm intermediate at best...)

 saveIssue() {
  Promise.all(
    this.imagePreviews.map(async image => {
      return await this.uploadImages(image);
    })
  ).then(() => {
    this.uploadData();
  });
},
uploadData() {
  let self = this;
  db.collection("issues")
    .add(self.issue)
    .then(docRef => {
      self.$router.push({
        name: "ReportPage",
        params: {
          issueId: docRef.id
        }
      });
    })
    .catch(error => {
      console.error(error);
    });
},
uploadImages(image) {
  const storageRef = storage.ref();
  let self = this;
  return new Promise((resolve, reject) => {
    let imageName = uuidv1();
    let fileExt = image.fileName.split(".").pop();
    let uploadTask = storageRef
      .child(`images/${imageName}.${fileExt}`)
      .putString(image.base64String, "data_url");
    uploadTask.on("state_changed", {
      error: error => {
        console.error(error);
        reject(error);
      },
      complete: () => {
        uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
          self.issue.images.push(downloadURL);
          resolve();
        });
      }
    });
  });
}
Related