0
const p1 = new Promise((_, reject) => {
  reject(1);  
}).then((r)=>console.log(r)).catch((r)=>console.log(r)); 

const p2 = new Promise((resolve, _) => {
  resolve(10);  
}).then((r)=>console.log(r)).catch((r)=>console.log(r));

// output -> 10, 1

Shouldn't the output be 1, 10.Because the catch block of p1 is added to the microtask queue before then block of p2? Below example gives expected output of 1,10

const p1 = new Promise((_, reject) => {
  reject(1);  
}).catch((r)=>console.log(r))

const p2 = new Promise((resolve, _) => {
  resolve(10);  
}).then((r)=>console.log(r)).catch((r)=>console.log(r));
4
  • Apologies, I have updated the question with the correct values. Commented Sep 29, 2024 at 14:43
  • 1
    Did you realise that in the first code snippet, the then method returns a promise, and that only when that promise settles, the catch callback becomes relevant? Commented Sep 29, 2024 at 14:44
  • @trincot In the first code snippet, for p1. I don't think the code inside then will get executed since the promise is rejected, right? Once p1 is rejected , shouldn't its catch method get added to the microtask queue. Only after that p2 will execute and its then will get added to microtask queue? Commented Sep 29, 2024 at 14:57
  • @bored_coffee Please see the difference between .then(…, …) and .then(…).catch(…). Notice that in your code, you are calling .then() and .catch() on different promises - .then() returns a new promise Commented Sep 29, 2024 at 17:19

1 Answer 1

1

To illustrate how the microtask queue evolves through the execution of your script, I would first suggest some changes to your script, so we can more easily identify the promises and the callbacks that are involved:

// Assign all used callbacks to distinct variables (for easy reference)
const p1a_then = console.log;
const p1b_catch = console.log;
const p2a_then = console.log;
const p2b_catch = console.log;

// Use available short-cuts for immediately settling promises
// and assign each created promise to a separate variable (for easy reference)
const p1a = Promise.reject(1);
const p1b = p1a.then(p1a_then);
const p1  = p1b.catch(p1b_catch); 

const p2a = Promise.resolve(10);
const p2b = p2a.then(p2a_then);
const p2  = p2b.catch(p2b_catch);

Note how the promise chains were split over several statements/assignments, but this does not affect the chain itself: it still represents the same flow of execution, but with more variable references which we can use in analysing this flow.

With some simplifications, we could represent that execution flow like this:

step call stack action p1a p1b p1 p2a p2b p2 microtask queue
1 script p1a = Promise.reject(1) R
2 script p1b = p1a.then(p1a_then) R P p1a_catch/p1b
3 script p1 = p1b.catch(p1b_catch) R P P p1a_catch/p1b
4 script p2a = Promise.resolve(10) R F
5 script p2b = p2a.then(p2a_then) R P F P p1a_catch/p1b, p2a_then/p2b
6 script p2 = p2b.catch(p2b_catch) R P P F P P p1a_catch/p1b, p2a_then/p2b
7 - process microtask queue R P P F P P p1a_catch/p1b, p2a_then/p2b
8 p1a_catch (pass through) reject p1b R R P F P P p2a_then/p2b, p1b_catch/p1
9 - process microtask queue R R P F P P p2a_then/p2b, p1b_catch/p1
10 p2a_then log(10), fulfill p2b R R P F F P p1b_catch/p1, p2b_then/p2
11 - process microtask queue R R P F F P p1b_catch/p1, p2b_then/p2
12 p1b_catch log(1), fulfill p1 R R F F F P p2b_then/p2
13 - process microtask queue R R F F F P p2b_then/p2
14 p2b_then (pass through) fulfill p2 R R F F F F
15 - process microtask queue R R F F F F

Some comments on this table:

  • The last column lists the entries in the microtask queue, with "function/promise" notation: when that microtask entry is consumed, the function will be called and its return value will be used to resolve/reject the promise mentioned after the slash.

  • That column sometimes lists functions that don't exist, like p1a_catch. This is to indicate that although this hander was not provided in the code, there is still an action to perform: the default action ("pass through") will reject promise p1b.

  • The columns near the right give the state of each promise: [P]ending, [F]ulfilled, [R]ejected.

Be aware that a callback is only queued in the microtask queue when the promise, on which this callback was attached, has settled. So for example, in step 3 the callback p1b_catch is not added to the microtask queue yet because p1b is not yet settled. This may be a surprise, but for p1b to settle, we first need p1a_then to execute, as that determines how p1b will resolve, and that execution will only happen asynchronously, i.e. later. In general, a then/catch method call will always return a pending promise, no matter what the state is of the promise on which you call that then/catch method.

I hope this clarifies it.

Sign up to request clarification or add additional context in comments.

1 Comment

"That column sometimes lists functions that don't exist" - I'd name these return and throw respectively, or maybe just annotate them with -

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.