Solving the mystery of Promise *catch* method - and learning more about the *then* on the way
Tags: javascript, webdev, codenewbie
Published: 2020-08-19 10:38:58

`catch` is a well known method for handling errors in Promise code. It's easy to learn and simple to use. 

But I have noticed that many programmers who only know Promises superficially, think that `catch` is the only way to handle errors in Promises code, or at least that it is always the preferable one.

And this is simply not true. I use `catch` very often and it definitely has its place, but in this article I want to prove to you that in order to handle errors in more subtle ways, you will sometimes need other means of dealing with exceptions.

This will teach us not only about the `catch` itself, but will also unveil interesting details about the `then` method!

So in this article we will begin by learning more about the `then` method first. Knowing it deeply will allow us to solve the "mystery" of `catch` - what it really is, how exactly does it work and when it should and shouldn't be used.

Let's begin!

## Intricacies of then

In real world application, the most common way to use `then` method is to simply pass it one argument - a callback function:

```js
somePromise.then(result => {
   // do something with `result`
   return newResult;
});
```

When `somePromise` resolves (for example a HTTP request finishes), our callback passed to `then` gets called with a value to which `somePromise` have resolved (for example JSON that we received from that HTTP request).

We can do whatever we want with the result inside the callback and optionally we can return some `newResult`.

This pattern is bread and butter of programming with Promises and that's why people believe that is really all you can do with `then`.

But then (sic!) comes an awkward moment. We make a HTTP request and we want to handle possible errors from that request, but we also have the case where we want to *throw* an error, for example when validation of the incoming JSON fails:

```js
httpRequest
    .then(jsonResult => {
        if (!isValid(jsonResult)) {
            throw new Error('This JSON is bad!');
        }
        // if JSON is valid, simply do something with it
    });
```

On this snippet, if the JSON is invalid, we will throw an error, which will get propagated further. That's what we want.

But also if there are any errors coming directly from `httpRequest`, they will be propagated as well. This we *don't* want. We want to handle those errors, but only those.

So what would be the solution? Many programmers who know `then`, know also about `catch` method. So probably the first attempt would look something like this:

```js
httpRequest
    .then(jsonResult => {
        if (!isValid(jsonResult)) {
            throw new Error('This JSON is bad!');
        }
        // if JSON is valid, simply do something with it
    })
    .catch(httpRequestError => {
        // handle somehow the HTTP request error
    });
```

This however doesn't work as we want.

Yes, all the errors from `httpRequest` will be caught and handled, but also all the errors coming from our `then` callback, including validation error, will be caught as well! 

And not only they will be caught, they will also be handled just like HTTP errors, because our `catch` callback is only prepared for those kinds of exceptions. This might in turn cause even more troubles in the error handling function and result in difficult to track bugs.

So the second thought might be to move `catch` method *above* the then method:

```js
httpRequest
    .catch(httpRequestError => {
        // handle somehow the HTTP request error
    })
    .then(jsonResult => {
        if (!isValid(jsonResult)) {
            throw new Error('This JSON is bad!');
        }
        // if JSON is valid, simply do something with it
    });
```

This is quite worrying solution, because at the beginning it will *seem* to work. If HTTP request resolves correctly, `then` method will be called as intended. If JSON validation fails, the error will be thrown and it will not be caught by any `catch`, just as we want.

However if the HTTP request fails, `catch` callback will be called. What will happen next is that the `then` method will be called right after!

If we don't return anything in our `catch` callback, the `then` callback will called with an `undefined` value:

```js
httpRequest
    .catch(httpRequestError => {
        // we are handling an error, but not
        // returning anything there
    })
    .then(jsonResult => {
        // if `httpRequest` threw an error,
        // this callback will be called,
        // with `jsonResult` having value `undefined`
    });
```

We might mitigate that, by simply bailing out from executing the `then` callback when its argument is `undefined`:

```js
httpRequest
    .catch(httpRequestError => {
        // handle somehow the HTTP request error
    })
    .then(jsonResult => {
        if (!jsonResult) {
            return;
        }

        if (!isValid(jsonResult)) {
            throw new Error('This JSON is bad!');
        }
        // if JSON is valid, simply do something with it
    });
```

This will work, but it's still kind of awkward and verbose. We simply don't want to call a callback handling JSON when we don't have a JSON to handle! So how would we do that?

That's exactly where the second argument to `then` comes with the help. The second argument of `then` method is also a callback, but it is an error handling callback. It will be called only when some Promise higher in the call chain throws an error which doesn't get caught and handled before.

So let's rewrite our example:

```js
httpRequest
    .then(
        jsonResult => {
            if (!isValid(jsonResult)) {
                throw new Error('This JSON is bad!');
            }
            // if JSON is valid, simply do something with it
        },
        httpRequestError => {
            // handle somehow the HTTP request error
        }
    );
```

It's cleaner, there is less code and we don't have to do any awkward `undefined` checks.

And, indeed, it works just as we want. The trick here is that the error handling function passed to `then` only reacts to errors that happen earlier in the call chain, not errors that happen in the `then`s first callback.

So in this example all the errors coming from `httpRequest` will be caught, but our validation error, happening in the callback, will not.

Furthermore, `then` will always call only one of the two callbacks. If everything goes right, it will simply call the first callback, as usual. If there is an unhandled exception higher in the chain, it will call only the second callback.

So we don't have to do any `if`s in the first callback. If we don't get a proper JSON result from the `httpRequest`, the JSON handling function will simply never be called.

Nice, isn't it?

## Default callbacks of then method

We are getting closer to solving the mystery of `catch`.

In order to finally answer that question, we need to take just a last, closer look at arguments that can be passed to the `then` method. 

We already showed that `then` accepts a second, optional argument. But it might be a surprise to you that the first argument is... optional as well!

It turns out, that you can think of both the first and the second arguments of `then` as having default values, which will be used if you don't provide any function.

The default function for the first argument is:

```js
result => result
```

and the default function for the second argument is:

```js
error => throw error;
```

It means that if you don't provide the first argument to the `then`, the method will simply take the value from the previous Promise and pass it on further.

On the other hand, if the previous Promise throws an error, the default error handling function of `then` will simply rethrow that error.

These are very sensible default behaviors, behaving so intuitively, that sometimes programmers don't even think about their existence.

## Solving the mystery of catch

With all this knowledge, we are at the moment when we can talk more about the `catch` method itself. 

It is a method that is, as we said before, a go to method for error handling for the most of the JavaScript programmers.

But do those that use it really understand how it works? After all, it seems that `then` method has already built-in error handling. How in that case `catch` relates to `then`?

The thing you have to think about is how `catch` behaves when the Promise, to which it's attached to, *doesn't* throw an error:

```js
const somePromiseWithCatch = Promise.resolve(5)
    .catch(error => console.log(error);
```

If `Promise.resolve(5)` would throw an error, this error would be logged to the console.

But it doesn't - `Promise.resolve(5)` immediately resolves to a number 5. So what result we will get in the end? How the catch will behave here with a Promise that doesn't throw any errors?

Let's attach a `then` to this newly constructed Promise:

```js
somePromiseWithCatch.then(value => console.log(value));
```

As you surely expected, after running this code number 5 gets printed to the console. So what `catch` does, is that it simply returns the same value as the previous Promise, as long as that Promise didn't throw any error.

All this information now should be enough for you to solve the mystery of `catch` by yourself.

What is `catch`? 

It's simply a `then` method without the first argument!!!

Indeed, the two following examples work in exactly the same way!

```js
somePromise.catch(error => { 
    /* somehow handle the error */ 
});
```

```js
somePromise.then(undefined, error => { 
    /* somehow handle the error */ 
});
```
Note how we passed `undefined` as a first argument to `then` so that it's default callback function is used.

We might have as well write:

```js
somePromise.then(result => result, error => { 
    /* somehow handle the error */ 
});
```

which would again result in the same behavior.

And if you still don't believe me that it can be that simple, just take a look at how `catch` is described in the EcmaScript Standard:

![catch description in EcmaScript Standard](https://dev-to-uploads.s3.amazonaws.com/i/9yblirutxkxktgtn36ah.png)

## Conclusion

In this article we solved the "mystery" of `catch`. We showed that it is not a completely original method, but merely a tiny wrapper for a `then` method, which we could easily write ourselves.

Obviously it's so handy to use, that it was added to the native Promises, to make our programming cleaner. After all it's easier to catch the `catch` with your eye among the many lines of `then` calls.

But on the way we have seen that sometimes it is beneficial to use error handling built-in into `then` method directly, because it can give you more fine tuned control over which errors you want to handle and which ones you don't.

I hope this article gave you a deeper understanding of both `catch` and `then` methods.

If you enjoyed this article, considered following me on [Twitter](https://twitter.com/m_podlasin), where I am regularly posting articles on JavaScript programming.

Thanks for reading!

<span>(Cover Photo by <a href="https://unsplash.com/@acfb5071?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Keith Johnston</a> on <a href="https://unsplash.com/s/photos/baseball-catch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>)</span>