# Promise anti-pattern

Source: https://tpiros.dev/blog/promise-anti-pattern

If you're working with JavaScript promises, you've probably noticed there are two ways to catch promise rejections: the `.catch()` method, or passing an error handler function to `.then()`.

Passing a second argument to `.then()` is considered a promise anti-pattern. And there's a good reason for it.

To see why, let's create a sample function that mimics an API call. It returns profile information of a user:

```javascript
function getProfile() {
  const person = {
    name: 'dave',
    age: 22,
    address: {
      city: 'London',
    },
  };
  return new Promise((resolve, reject) => {
    if (!person) {
      reject();
    }
    resolve(person);
  });
}
```

The function returns the `person` object defined within its own scope. We're mimicking a call to an API that hands us back an object.

## Handling errors with `.then()`

Let's see what happens when we try to capture errors using the callback pattern, passing both a success and an error handler to `.then()`:

```javascript
getProfile().then(
  (response) => {
    delete response.address;
    console.log(response.address.city);
  },
  (error) => console.log(`Scary error happened: ${error}`)
);
```

Notice we're deliberately deleting the address property from the person object, then trying to access it. We're manufacturing an error on purpose. The second parameter is our error handler, a simple log statement that should show us the error.

Running this produces some surprises. You'll never see the 'Scary error happened' message. Instead you'll get an unhandled promise rejection: `UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'city' of undefined`.

That's odd, because we've got an error handler right there. But here's the catch (pun intended): the error generated in the success handler part of `.then()` is **not** captured by the error handler. That error handler can only catch errors that `getProfile()` itself generates. Let's prove it by breaking `getProfile()`:

```javascript
function getProfile() {
  const personx = {
    name: 'dave',
    age: 22,
    address: {
      city: 'London',
    },
  };
  return new Promise((resolve, reject) => {
    if (!person) {
      reject();
    }
    resolve(person);
  });
}
```

(Now we've got a `personx` object, which means `person` is undefined and `reject()` fires.)

Re-running the previous example, the error handler kicks in as expected: `Scary error happened: ReferenceError: person is not defined`.

## Handling errors using `.catch()`

To avoid nasty surprises, use `.catch()` after your `.then()`. The catch statement captures errors from the request and errors that happened inside the `.then()` block itself:

```javascript
getProfile()
  .then((response) => {
    delete response.address;
    console.log(response.address.city);
  })
  .catch((error) => console.log(`Scary error happened: ${error}`));
```

We still see the `Scary error happened: ReferenceError: person is not define` message. Now change `personx` back to `person` and run the example again. This time there's no unhandled promise rejection warning. Instead we get our error message: `Scary error happened: TypeError: Cannot read property 'city' of undefined`.

## Summary

Here's the pseudo-code breakdown:

```javascript
successfulPromiseRequest()
  .then((response) => {
    // errors here are captured by .catch()
  })
  .catch((error) => console.log(error));

successfulPromiseRequest().then(
  (response) => {
    // errors here will go unhandled
  },
  (error) => console.log(error)
);

failedPromiseRequest()
  .then((response) => {
    // errors for the request are captured by .catch()
  })
  .catch((error) => console.log(error));

failedPromiseRequest().then(
  (response) => {
    // errors for the request will be handled
  },
  (error) => console.log(error)
);
```

Since you can chain multiple `.then()` statements with Promises, remember that `.catch()` catches errors from any of them:

```javascript
successfulPromiseRequest()
  .then((response) => {
    return failedSuccessfulPromiseRequest(); //will be caught by catch()
  })
  .then((response) => {
    return successfulPromiseRequest();
  })
  .catch((error) => console.log(error));
```
