Search code examples
javascriptpromise

Why does the following code resolves first instead of rejection?


[Promise.reject(42), Promise.resolve(4)].forEach((item, i) => {
  Promise.resolve(item)
    .then(() => console.log("resolve", i))
    .catch(() => console.log("reject", i));
});

Expected output:

reject 0
resolve 1

Actual output:

resolve 1
reject 0

Solution

  • Each call to .then or .catch creates a new promise, which is dependent on the promise that came before. So your code is building two chains of promises. The reason for the behavior you're seeing is that the logs are at different spots in the chain. The log for the reject case is one layer deeper in the chain than the log for the resolve case.

    The microtask queue controls which callback is going to execute next, and since both top level promises are starting in a settled state, there are going to be two entries in the queue. Once the first callback finishes, the promise below it can resolve and put itself into the queue, but it has to go behind the promise that's already in the queue from the other stack. In other words, resolving the promises is going to zig zag between the two stacks: reject1 -> resolve1 -> reject2 -> resolve2 -> reject3 -> resolve3 (the bold ones are where the logs are).

    So it might be looking at the rejected stack first at each layer, but it has to go deeper before it will get to the .catch which has the console.log.

    If you did the following change, then it would work as you expect (.then takes a second parameter to handle the catch case):

    [Promise.reject(42), Promise.resolve(4)].forEach((item, i) => {
      Promise.resolve(item)
        .then(
          () => console.log("resolve", i),
          () => console.log("reject", i)
        )
    });