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.