1

I'm learning Promises so that I can understand better before I try to use Firebase. I am new and I've been reading the following in regard to catch():

  • Link one: An article with some exercises
  • Link two: A question in SO in regard to why we always need a catch() after Promise chain
  • Link three: A question in SO in regard to the difference of catch() and then()

From what I've read, I have taken the following conclusions:

  • catch() is necessary at in every Promise chaining in case "unexpected exceptions" occurs. It seems like these "unexpected exceptions" can be detected by the failureHandler of my then. However, it cannot distinguish between "normal failure" from these types of failures. I assume that one of those "unexpected exceptions" is when you're trying to access some property of a null element.
  • It seems like I can also do chaining of then(successHandler, failureHandler) and then proceed with a catch() block to allow finer control, as mentioned in link two. This is useful when I want to do something else when something fails ("normal failure" in this case, not the "unexpected exceptions") and pass the rejected Promise to the next then to process, thus potentially yielding very different results from the result had the failed part succeeded. I can also catch "unexpected exceptions" by using catch() at the end of the chain in case something failed inside my successHandler or failureHandler.

As you can see from my conclusions, I have very little understanding of what errors might occur. I mentioned null exception as one of the examples of "unexpected exceptions" (is this assumption even correct?). However, what other errors do failureHandler detect and what other "unexpected exceptions" do catch() detect?

I also mentioned above that [then] cannot distinguish between normal failure from these types of failures. Is that correct? If it is, why is it important?

EDIT

After reading some more, it seems like if a Promise is rejected on the top of the chain, the thens following are ignored and I immediately go to the catch() block. This means that my conclusion above: This is useful when I want to do something else when something fails and pass the rejected Promise to the next then to process is incorrect. If that is the case, if I already have a catch() at the end of my chain, I no longer need a failureHandler for each of my then block. However, it is mentioned in link three:

The argument is that usually you want to catch errors in every step of the processing, and that you shouldn't use it in chains. The expectation is that you only have one final handler which handles all errors - while, when you use the "antipattern", errors in some of the then-callbacks are not handled.

However, this pattern is actually very useful: When you want to handle errors that happened in exactly this step, and you want to do something entirely different when no error happened - i.e. when the error is unrecoverable. Be aware that this is branching your control flow. Of course, this is sometimes desired.

I took my conclusion that the rejected Promise will be passed to the next then to process because I read the above. So what does and you want to do something entirely different when no error happened - i.e. when the error is unrecoverable mean?

Richard
  • 7,037
  • 2
  • 23
  • 76

1 Answers1

1

From the MDN I understand that there is no real difference between the two methods of declaring the rejection handler.

For success, it's obvious that we will get to the fulfillment handler:

var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('success');
  }, 300);
}).then(function(success) {
  console.log(0, success)
}, function(failure) {
  console.log(1, failure)
}).catch(function(failure) {
  console.log(2, failure)
});

For rejection we'll get to the first rejection handler.
When rejecting the promise:

var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    reject('failure');
  }, 300);
}).then(function(success) {
  console.log(0, success)
}, function(failure) {
  console.log(1, failure)
}).catch(function(failure) {
  console.log(2, failure)
});

When throwing an error:

var promise = new Promise(function(resolve, reject) {
  throw "throw";
}).then(function(success) {
  console.log(0, success)
}, function(failure) {
  console.log(1, failure)
}).catch(function(failure) {
  console.log(2, failure)
});

Notice how the catch block is being ignored because it's being chained to the promise of the first handler (fulfillment or rejection).
If we change it to reject or re-throw we'll get to the handler in the catch.

var promise = new Promise(function(resolve, reject) {
  throw "re-throw";
}).then(function(success) {
  console.log(0, success)
}, function(failure) {
  console.log(1, failure)
  throw failure;
}).catch(function(failure) {
  console.log(2, failure)
});

This image, from the above link, describes it well. Image from MDN link

Oram
  • 1,589
  • 2
  • 16
  • 22