DEV Community

ValPetal Tech Labs
ValPetal Tech Labs

Posted on

Question of the Day #4 [Talk::Overflow]

This post explains a quiz originally shared as a LinkedIn poll.

πŸ”Ή The Question

console.log('1');

Promise.resolve()
  .then(() => {
    console.log('2');
    return Promise.resolve('3');
  })
  .then((val) => {
    console.log(val);
    Promise.resolve('4').then(console.log);
  })
  .then(() => {
    console.log('5');
  });

console.log('6');
Enter fullscreen mode Exit fullscreen mode

Hint: Consider the microtask queue and how promise resolution affects the order of execution.

πŸ”Ή Solution

Correct Answer: B) 1, 6, 2, 3, 5, 4

The output will be: 1, 6, 2, 3, 5, 4

🧠 How this works

This quiz demonstrates a subtle but critical aspect of JavaScript's promise resolution and microtask queue behavior. The key mechanism here is how returning a promise from a .then() handler affects the microtask queue scheduling.

When you return a promise from a .then() handler, JavaScript doesn't immediately schedule the next .then() handler. Instead, it waits for that returned promise to resolve, and only then does it schedule the next handler as a new microtask. This creates an additional microtask queue cycle.

The non-obvious part is in the second .then() handler: when we call Promise.resolve('4').then(console.log), we're creating a new promise chain that gets scheduled as a microtask, but this happens after the current promise chain has already scheduled its next handler (console.log('5')).

πŸ” Line-by-line explanation

  1. console.log('1') executes immediately β†’ outputs 1

  2. Promise.resolve() creates a resolved promise and schedules its .then() handler as a microtask

  3. console.log('6') executes immediately (synchronous code runs first) β†’ outputs 6

  4. Microtask queue processing begins:

    • First .then() handler executes: console.log('2') β†’ outputs 2
    • Returns Promise.resolve('3') - this is the critical part
  5. Promise resolution behavior: When a .then() handler returns a promise, JavaScript waits for that promise to resolve before scheduling the next handler. Since Promise.resolve('3') is already resolved, it schedules the next .then() handler as a new microtask (not in the current microtask queue cycle)

  6. Current microtask queue cycle completes, then the next cycle begins:

    • Second .then() handler executes: receives '3', outputs 3
    • Creates Promise.resolve('4').then(console.log) - this schedules a new microtask
    • Returns undefined (implicit return)
  7. Third .then() handler is scheduled as a microtask (because the previous handler returned undefined, not a promise, so it's scheduled immediately in the same cycle):

    • Executes: console.log('5') β†’ outputs 5
  8. Next microtask queue cycle:

    • The Promise.resolve('4').then(console.log) microtask executes β†’ outputs 4

The misleading part: Many developers expect 4 to appear before 5 because it's created earlier in the code. However, because the second .then() handler returns undefined (not a promise), the third .then() handler gets scheduled in the same microtask cycle, while the Promise.resolve('4') chain gets scheduled for the next cycle.

πŸ”Ή Key Takeaways

  1. Returning a promise from .then() adds an extra microtask cycle: When a .then() handler returns a promise, JavaScript waits for that promise to resolve before scheduling the next handler, creating a new microtask queue cycle.

Follow me for JavaScript puzzles and weekly curations of developer talks & insights at Talk::Overflow: https://talkoverflow.substack.com/

  1. Microtask queue processing is atomic per cycle: All microtasks scheduled in the current cycle execute before the next cycle begins, but promises returned from handlers create new cycles.

  2. Nested promise chains schedule independently: When you create a new promise chain inside a .then() handler (like Promise.resolve('4').then(...)), it gets scheduled for a future microtask cycle, not the current one.

  3. Return value matters: Returning undefined vs. returning a promise from a .then() handler affects when the next handler is scheduled - undefined schedules immediately in the current cycle, while a promise waits for resolution.

  4. This is why async/await can be clearer: Using async/await makes the execution order more explicit and easier to reason about, though the underlying microtask behavior remains the same.

Follow me for JavaScript puzzles and weekly curations of developer talks & insights at Talk::Overflow: https://talkoverflow.substack.com/

Top comments (0)