shaimo
shaimo

Reputation: 376

How to avoid nesting promises with firebase transaction?

In an onDelete trigger I'm running a transaction to update some object. I now need to do some cleanup and delete some other objects before running that transaction. After adding the cleanup code I'm getting a warning about nesting promises which I don't know how to get rid of. Here is a snippet:

exports.onDeleteAccount = functions.firestore
.document('accounts/{accountID}')
.onDelete((account, context) => {
  // First do the cleanup and delete addresses of the account
  const query = admin.firestore().collection('account_addresses').where('accountID', '==', account.id);
  return query.get().then(addresses => {
    var promises = [];
    addresses.forEach(address=>{
      promises.push(address.ref.delete());
    })
    return Promise.all(promises);
  }).then(()=> {
    // Then run the transaction to update the account_type object
    return runTransaction(transaction => {
      // This code may get re-run multiple times if there are conflicts.
      const acc_type = account.data().type;
      const accountTypeRef = admin.firestore().doc("account_types/"+acc_type);
      return transaction.get(accountTypeRef).then(accTypeDoc => {
        // Do some stuff and update an object called users
        transaction.update(accountTypeRef, {users: users});
        return;          
      })
    })
  })
  .catch(error => {
    console.log("AccountType delete transaction failed. Error: "+error);
  });
})

Upvotes: 1

Views: 612

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83093

I don't think the problem comes from the Transaction but from the forEach loop where you call delete(). You should use Promise.all() in order to return a single Promise that fulfills when all of the promises (returned by delete()) passed to the promises array have been fulfilled, see below.


In addition, you do runTransaction(transaction => {...}) but runTransaction is a method of Firestore. You should do admin.firestore().runTransaction(...).


Therefore, the following should do the trick:

exports.onDeleteAccount = functions.firestore
    .document('accounts/{accountID}')
    .onDelete((account, context) => {
        // First do the cleanup and delete addresses of the account
        const query = admin.firestore().collection('account_addresses').where('accountID', '==', account.id);
        return query.get()
            .then(addresses => {
                const promises = [];
                addresses.forEach(address => {
                    promises.push(address.ref.delete());
                })
                return Promise.all(promises);
            }).then(() => {
                // Then run the transaction to update the account_type object
                return admin.firestore().runTransaction(transaction => {
                    // This code may get re-run multiple times if there are conflicts.
                    const acc_type = account.data().type;
                    const accountTypeRef = admin.firestore().doc("account_types/" + acc_type);
                    return transaction.get(accountTypeRef).then(accTypeDoc => {
                        // Do some stuff and update an object called users
                        transaction.update(accountTypeRef, { users: users }); 
                    })
                })
            })
            .catch(error => {
                console.log("AccountType delete transaction failed. Error: " + error);
            });
    })

Upvotes: 2

Related Questions