3

I'm building a web application for creating retail signage. Firestore holds information about the signs, and I want to be able to remove all of the signs if a user clicks on 'Remove all Signs.' A dialog will then appear which will ask the user to confirm deleting all signs in the database. If they confirm, then all the signs should then be deleted from Firestore.

The document ID of each sign is stored in an array called signIds. I want to be able to iterate through signIds and delete each document from the signs collection in Firestore. Once all the Promises have resolved, I want to return a Promise and resolve it in the event handler in mounted().

I've tried returning a Promise at different places in the code, to no avail. I've also tried using async and await but that seemed to have the same issue.

data() {
        return {
            signIds: [],
        }
},
methods: {
    showModal(message, type) {
        this.$root.$emit('openModal', {
            closed: false,
            text: message,
            type: type
        })
    },
    emptyQueue() {
        let self = this;
        let deleted = 0;

        for (let id of self.signIds) {
            database.collection("signs").doc(id).delete()
                .then(() => {
                    console.log("Document successfully deleted!");
                    deleted++;
                }).catch((error) => {
                    console.error("Error removing document: ", error);
                });
        }

        // Once all signs are deleted, return new Promise
        return new Promise((resolve, reject) => {
            return (deleted === self.signIds.length) ? resolve() : reject(new Error('An error occurred while deleting signs.'));
        });
    }      
},
created() {
        // Get and store the document id for each sign
        database.collection("signs").get()
            .then(snapshot => {
                snapshot.forEach(doc => {
                    this.signIds.push(doc.id);
                })
            });
    },
mounted() {
        // When the user clicks 'OK' on the dialog, delete all signs from the database
        this.emptyQueue()
            .then(() => {
                setTimeout(function() {
                    self.showModal('All signs were successfully removed.', 'success');
                }, 300);
            }).catch(() => {
                setTimeout(function() {
                    self.showModal('An error has occurred. Some signs were not removed.', 'error');
                }, 300);
            })
        }

I expect the new Promise to return after Firestore's Promises have resolved, but currently the new Promise is returned immediately after the for loop is finished.

HeadAdmiral
  • 111
  • 1
  • 8

3 Answers3

4

Your current iteration through signIds's Promises is not being chained with anything else - the .delete() Promises aren't being used anywhere at the moment. It would be better to .map each id to a Promise in an array, then use Promise.all on that array. If errors are handled in the consumer, there's no need to catch in emptyQueue just to throw a new error - instead, just return the Promise chain alone, and avoid the explicit Promise construction anti-pattern:

emptyQueue() {
  return Promise.all(
    this.signIds.map(id => (
      database.collection("signs").doc(id).delete()
    ))
  );
}

If any one of the .delete() calls result in an error, that error will percolate up to your

this.emptyQueue()
  // ...
  .catch(() => {
    ...
  });

section automatically via the Promise.all.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

Making use of write batch may answer your needs https://firebase.google.com/docs/reference/js/firebase.firestore.WriteBatch

1

You should be able to do that using batch, with a code that should look something like the following:

// start a batch
var batch = database.batch();

for (let id of self.signIds) {
  // for each, add a delete operation to the batch
  batch.delete(database.collection("signs").doc(id));
}

// Commit the batch
batch.commit();
quirimmo
  • 9,800
  • 3
  • 30
  • 45