简体   繁体   中英

Breaking promise chain with thrown exception

Currently, if there is an error caught in asyncFunction1() s promise callback, the app will correctly throw the 'Problem A' exception. However, this is passed through the promise chain and the app will eventually see 'Problem B', which means the app is showing the wrong error to the user.

I effectively need to abort execution and break the chain whilst throwing the relevant error. How can I do this?

The HttpsError class information can be found here: https://firebase.google.com/docs/reference/functions/functions.https.HttpsError

It explicitly mentions:

Make sure to throw this exception at the top level of your function and not from within a callback, as that will not necessarily terminate the function with this exception.

I seem to have fallen into this trap, but do not know how to work around it. If someone could help me refactor the code so that I can effectively catch and handle these errors properly that would be much appreciated.

exports.charge = functions.https.onCall(data => {
  asyncFunction1()
    .then(() => {
      asyncFunction2();
    })
    .catch((err) => {
      throw new functions.https.HttpsError(
        'not-found',
        'Problem A'
      );
    })
    .then(() => {
      asyncFunction3();
    })
    .catch((err) => {
      throw new functions.https.HttpsError(
        'not-found',
        'Problem B'
      );
    })
});

There are a number of different ways to approach this:

  1. You can have each async function just set the appropriate error when it rejects so you don't have to manually add the right error in your own .catch() .

  2. You can test in the last .catch() to see if an appropriate error has already been set and just rethrow it if so rather than override it with another error.

  3. You can put the last .catch() on the asyncFunction3() call directly instead of on the whole chain like this so you're targeting only a rejection from that function with that error code:

Modified code:

exports.charge = functions.https.onCall(data => {
    return asyncFunction1().then(() => {
        return asyncFunction2();
    }).catch((err) => {
        // set appropriate error for rejection in either of the first two async functions
        throw new functions.https.HttpsError('not-found', 'Problem A');
    }).then(() => {
        return asyncFunction3().catch((err) => {
            // set appropriate error for rejection in asyncFunction3
            throw new functions.https.HttpsError('not-found', 'Problem B');
        });
    });      
});

Note: I've also added several return statements to make sure promises are being linked into the chain and returns from the exported function. And, I've condensed the logic to make it easier to read.


This might also be a case for async/await (though I'm not entirely sure if functions.https.onCall() allows this or not):

exports.charge = functions.https.onCall(async (data) => {
    try {
        await asyncFunction1()
        await asyncFunction2();
    } catch(e) {
        throw new functions.https.HttpsError('not-found', 'Problem A');
    }
    try {
        await asyncFunction3();
    } catch(e) {
        throw new functions.https.HttpsError('not-found', 'Problem B');
    }
});

Would something like this work?

exports.charge = functions.https.onCall(data => {
   return Promise.resolve().then(
      () => {
          return asyncFunction1();
      }).then(
      () => {
          return asyncFunction2();
      }).then(
      () => {
          return asyncFunction3();
      }).catch(
      err => {
          throw new functions.https.HttpsError(err);
      });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM