I’m trying to understand how promises work under the hood in JS and faced with this example:
const promise = Promise.reject();
promise
.catch(() => console.log(1));
promise
.catch(() => console.log(2));
// Output:
// 1
// 2
const promise = Promise.reject();
promise
.catch(() => console.log(1))
.catch(() => console.log(2));
// Output:
// 1
Why do we have different outputs in similar examples?
Why catch
is called second time in the first example?
Because promise is already rejected after the first catch, so it should be logical that the second catch should not be called in both cases…
I think the main reason here is how microtasks queue works but I’m not sure.
2
Answers
This behavior is due to the microtask queue and how promises chaining works in JavaScript.
in the first example
Promise.reject() creates a rejected promise. This immediately schedules the catch handlers attached to the promise in the microtask queue for execution after the current synchronous code has finished.
promise.catch(() => console.log(1));
This schedules a microtask for the first catch handler to handle the rejection and log 1.
promise.catch(() => console.log(2));
This schedules another microtask for the second catch handler to handle the same rejection and log 2
in the second example
Promise.reject() creates a rejected promise. The rejection is scheduled to be handled in the microtask queue by the first catch handler.
promise.catch(() => console.log(1))
The first catch is chained to the promise. When the rejection is handled here and logs 1, the promise returned by this catch becomes a resolved promise.
.catch(() => console.log(2))
This catch is attached to the promise returned by the previous catch. Since the previous catch has resolved the promise, this catch is not triggered.
It boils down to this:
You can attach multiple callbacks to one promise. Yes, you can have several success and failure callbacks on the same promise, and they’ll all be called once the promises either resolves or rejects.
In a promise chain, a catch in the chain will catch the propagation of rejections and continue on the successful path afterwards.
Your first case is an example of a promise chain fork:
You attach two catches to the same promise. Both will be called if the promise rejects. If you attach more callbacks to one or both catches, they’ll be called depending on the result of your
catch
handler.Your second case is a simple chain:
The first
catch
handler is called because the promise rejected. That handler doesn’t produce any new errors, so the chain continues to the next success handler. Of which there is none, so nothing else happens.