1

Please consider this code:

function testPromise() {
  resolver = function(resolve, reject) {
    resolve('foo');
    console.log('@resolver');
  }
  setTimeout(() => console.log('Timeout'), 0);
  console.log('Before');
  const p = new Promise(resolver);
  console.log(p);
  p.then((value) => console.log('@then1:', value));
  p.then((value) => console.log('@then2:', value));
  console.log('After');
}

Running this function gives the following output in Firefox console:

Before
@resolver
Promise { <state>: "fulfilled", <value>: "foo" }
After
@then1: foo
@then2: foo
Timeout

Although the promise executor doesn't do anything asynchronous and calls the resolve callback parameter right away, and the console output confirms that the promise state is fulfilled, nevertheless the callback function that is passed to promise's then() method is called later, after execution of the currently running code is finished. I could get the same results if the resolve() call would be put in setTimeout: setTimeout(() => resolve('foo'), 0);

I'd like to understand more about this behavior: when exactly this then callback is called? Immediately after the currently executed code exits? Or there could be some other queued-up code that gets executed before? From the test code above, it can be observed that the zero-time timeout callback is executed later.

Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
Passiday
  • 7,573
  • 8
  • 42
  • 61
  • 2
    if you don't already know how, it's time to learn how to use a debugger. The console is an asynchronous environment and misleads many people about the actual behavior of asynchronous code. To "_I'd like to understand more about this behavior:_", load this code up in an IDE with a debugger, set a break point on the first line of the code, step the every single line of code. You'll need breakpoints inside any async code as the debugger will just skip over it otherwise. **That** is the way to understand precisely the order of operation. – Randy Casburn Feb 19 '21 at 19:54
  • 1
    Also, read this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#event_loop – Randy Casburn Feb 19 '21 at 20:00
  • 1
    "*Immediately after the currently executed code exits*" - yes, that. Of course it has to share that queue with other promise callbacks. – Bergi Feb 19 '21 at 21:06
  • @RandyCasburn A debugger will help you no better than `console.log` for following the execution order of this particular code. And neither will help to understand the general operating principle, as it's just a single example. – Bergi Feb 19 '21 at 21:08
  • See [here](https://stackoverflow.com/q/36932244/1048572) (and the linked questions there) for the how and why, and [this](https://stackoverflow.com/q/38752620/1048572) or [that](https://stackoverflow.com/q/36870467/1048572) for the exact interaction with `setTimeout`. – Bergi Feb 19 '21 at 21:15
  • @Bergi - with respect, I disagree. My experience is quite different. Properly stepping through this code will show precisely when each `console.log()` is made, in the order they are called. I have found that many, many times the limitation is a person's ability to interpret the code properly - as soon as they see it in the debugger the proverbial light goes on. I do think it has the potential to help (specifically when it comes to interpretation - one of the hardest things we do). – Randy Casburn Feb 19 '21 at 21:18
  • @RandyCasburn I think the OP understands perfectly fine in what order the code executes. They now ask *why* that execution order was chosen, what the general rules are. – Bergi Feb 19 '21 at 21:23
  • @Bergi - I suppose I was picking up on this question: "_when exactly this then callback is called?_" – Randy Casburn Feb 19 '21 at 21:29
  • @RandyCasburn you are right about `console.log` being non-ideal replacement for a debugger. I just wanted to show a piece of code that quickly demonstrates the problem. If I'd replace the `console.log` calls with something like `output.push(value)` I'd get exactly the same order. And my question focuses on wondering whether any other code could be executed before the `then` callbacks. Event the `setTimeout` code that is registered in event loop earlier still has to wait until the `then` callbacks are run. I am wondering how the `Promise` class achieves that. – Passiday Feb 20 '21 at 12:53
  • 1
    @Passiday In that case, have a look at https://stackoverflow.com/q/25915634 and https://stackoverflow.com/q/46375711 specifically – Bergi Feb 20 '21 at 14:00
  • @Bergi - you beat me to it! Ha. – Randy Casburn Feb 20 '21 at 14:19
  • I attempted an answer but it's better to delete a poor quality answer than argue of semantics and terminology. Essentially, every function call other than the callback in `setTimeout()` goes into the stack synchronously and are executed synchronously _in your code as written_. So `Timeout` prints last in the console. If you delay the call to `resolve()` with a timer it will print last because of when it gets placed onto the stack (it will be after rather than before the first `setTimeout()` callback. It gets placed in the list of timer callbacks after the first call to `setTimeout()`. – Randy Casburn Feb 20 '21 at 16:58
  • Ok, the concept of microtask and `queueMicrotask()` is what I was looking for. I wasn't aware of such way how to add tasks to the event queue that are executed before "normally" queued other events. – Passiday Feb 20 '21 at 18:20

0 Answers0