JavaScript Promises Then/Catch order

The order of the .then() and .catch() blocks matters.

Let's first take a look at the promise syntax.

Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  });

The above code will result in the following:

1
2
Promise{<fulfilled>: undefined}

As you can see, even though we don't return anything from first .then() block, the second one will still run. And in the end, it will return a fulfilled Promise with no value.

For rejected promises, the behaviour is similar:

Promise.reject()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  });

Returns

Promise{<rejected>: undefined}

A rejected Promise will return a rejected Promise, and skip all the .then blocks. It will also throw an Error. To prevent the error, we can use the catch block;

Promise.reject()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .catch(() => {
    console.log(3);
  });

This will result in the following:

3
Promise{<fulfilled>: undefined}

Now, as we caught the error, the first 2 .then blocks will not be called at all. Still, what in the end gets returned is a fulfilled Promise; the same as we saw in the first example. What this means is that a .catch on a rejected Promise will, by default, return a resolved Promise.

Promise.reject()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .catch(() => {
    console.log(3);
  })
  .then(() => {
    console.log(4);
  });
3
4
Promise{<fulfilled>: undefined}

Thus, this means chaining a .then after a .catch will result in the .then block being called, even if the Promise was initially rejected.

This does not seem self-evident initially, given we also have a .finally block, which indeed should be called for both a rejected and fulfilled promise: behavior very similar to a .then after a .catch. But, if you are creating more complex flows with perhaps multiple levels at which an error can be caught, this finally block may come in handy.

So, what if you do want to catch an error in a Promise multiple times? You may choose to throw an Error in the .catch handler, after which the following .then blocks will again not be executed, as the Promise remains to have a rejected status.

Promise.reject()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .catch((e) => {
    console.log(3);
    throw Error(e);
  })
  .then(() => {
    console.log(4);
  })
  .catch(() => {
    console.log(5);
  });
3
5
Promise{<fulfilled>: undefined}
Security of passwordsLifting state