Milan Ferus-Comelo
Milan Ferus-Comelo

Reputation: 740

Catching errors in async functions in an async function

I'm not completely sure if the title correctly describes my problem. Please correct me if I'm wrong :)

I would like to call a function to update a user's data in multiple databases/Firebase authentication (the function then calls multiple other async functions). I would like to do all the error handling in the parent function which calls updateUserData(). How can I do this?

This is my code:

async updateUserData(user: User) {
  // Update Email
  const currentUser = this.getCurrentUser();
  if(user.email && user.email != currentUser?.email)
    currentUser?.updateEmail(user.email)
  
  // Update Display Name
  if(user.displayName && user.displayName != currentUser?.displayName) 
    currentUser?.updateProfile({displayName: user.displayName})

  // Update User DB
  this.firestore
    .collection('users')
    .doc(user.uid)
    .set(user, { merge: true })

  // Update Public User DB
  const publicUserDBRef = this.firestore.collection('public-users').doc(user.uid);
  if(user.displayName && !user.settings.isPrivate) {
    publicUserDBRef
      .set({ uid: user.uid, displayName: user.displayName }, { merge: true })
  } else {
    publicUserDBRef
      .delete()
  }
}

I have tried using a try-catch block, adding .catch( error => { throw new Error(error.code) }) to all the async functions in updateUserData(), using return new Promise( (resolve, reject) => {...} (resolve() was called at the end of the function, after updating the Public User DB and reject was called in each .catch() block)

Upvotes: 1

Views: 313

Answers (2)

Nicholas Tower
Nicholas Tower

Reputation: 84982

.set and .delete return promises, but you're not doing anything with those promises. So neither this function, nor any function that calls this one will wait for them to finish. This also means a try/catch cannot catch any asynchronous errors they throw.

If you're ok with waiting for the first .set to complete before the .delete (or second set) happens, you just need to stick some awaits on your current code:

// Update User DB
await this.firestore
  .collection("users")
  .doc(user.uid)
  .set(user, { merge: true });

// Update Public User DB
const publicUserDBRef = this.firestore.collection("public-users").doc(user.uid);
if (user.displayName && !user.settings.isPrivate) {
  await publicUserDBRef.set(
    { uid: user.uid, displayName: user.displayName },
    { merge: true }
  );
} else {
  await publicUserDBRef.delete();
}

If you want both to happen in parallel, then I'd recommend creating an array of promises, then doing Promise.all with them, and either returning or awaiting that promise:

const promises = [];
// Update User DB
promises.push(
  this.firestore.collection("users").doc(user.uid).set(user, { merge: true })
);

// Update Public User DB
const publicUserDBRef = this.firestore.collection("public-users").doc(user.uid);
if (user.displayName && !user.settings.isPrivate) {
  promises.push(
    publicUserDBRef.set(
      { uid: user.uid, displayName: user.displayName },
      { merge: true }
    )
  );
} else {
  promises.push(publicUserDBRef.delete());
}

return Promise.all(promises);

Upvotes: 1

Denis Kotov
Denis Kotov

Reputation: 882

It is pretty simple, just add try catch around await-ed function:

async function parentFunction() {
  // Here was created user
  try {
    await updateUserData(user);
  } catch (e) {
    console.log(`e is ${e}`);
    // Some handling of the error
  }
  // ...
}

Upvotes: 0

Related Questions