3

I'm getting confused on how the following testcode would be put into the JS event loop.

var p1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve(1)
  }, 100)
});
var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve(2)
  }, 0)
});
p1.then((value)=>{console.log(value)})
p2.then((value)=>{console.log(value)})

From my understanding, wouldn't the setTimeout functions be synchronously called and then right after the two .then events be added into the microtask queue? But wouldn't this mean that at the end of the current iteration the two microtasks are ran before the resolve functions are even ran? Or is it that when resolve functions are called, all microtasks related to that promise are called at the end of the current iteration?

I think it would be best if somebody could just explain to me step-by-step on how the code is added into the event loop and then ran.

  • The only thing been put into the event loop is the `resolve` callbacks when the timeout has fired, and this is the macroTask. The promises get put in a microTask queue, this queue basically gets drained before the next event loop. A common miss-conception of promises is that there `async`, there not, there just a very useful tool for creating `async` code, when the microTask queue gets drained, it's been drained `synchronously`. – Keith Jun 06 '23 at 19:18
  • @Keith Thank you for your response. I think I understand now that the promises are first added to the microtask Queue and then at the end of the current loop their constructor functions are ran setting up a timer to add the resolve callbacks to the macrotask queue. What I still don't get is when we get to the .then() calls, does it automatically associate the provided callback with the later resolve callback that is going to be added? – Liangtao Hu Jun 06 '23 at 19:29
  • Promises don't make anything asynchronous. They are just a notification mechanism that indicates success or failure of some operation that's _already_ asynchronous. – Yousaf Jun 06 '23 at 19:35

1 Answers1

1

wouldn't the setTimeout functions be synchronously called

Yes, the setTimeout will be invoked synchronously.

and then right after the two .then events be added into the microtask queue?

No. Calling setTimeout schedules a task in the task queue to execute the callback, passed to the setTimeout call, when the timer expires. Even if you pass zero as the delay, the callback will be invoked asynchronously.

But wouldn't this mean that at the end of the current iteration the two microtasks are ran before the resolve functions are even ran?

Both promises will be resolved asynchronously. After the synchronous execution of the code ends, even loop can start processing the scheduled callbacks as a result of two setTimeout calls in your code.

It is only when a promise is resolved and their is a fulfilment handler registered for that promise, a job is queued in the micro-task queue to execute the fulfilment handler.


Following steps describe your code execution:

  1. Both promises are created as a result of Promise constructor calls. The function passed to the Promise constructor is called synchronously as part of promise creation process. As a result, two setTimeout callbacks are scheduled to be executed once their respective timer expires.

  2. After both promises are created one after the other, then methods are called on promises p1 and p2 respectively. This registers fulfilment handlers for both promises.

  3. At this point, the synchronous execution of the code has ended. Now the event loop will process any callbacks scheduled in the task queue. Both setTimeout callbacks will be put into the task queue once their respective timer expires.

  4. Once both setTimeout callbacks are executed, both promises are resolved one after the other. As soon as any promise is resolved, to execute its fulfilment handlers, a job is enqueued in the micro-task queue. In total there will be two jobs enqueued in the micro-task queue one after the other to execute fulfilment handlers of both promises.

Yousaf
  • 27,861
  • 6
  • 44
  • 69
  • When the program gets to the two .then() lines, does it basically automatically associates the provided resolve callbacks to their promises so that once the timers end, it knows which callback is for which promise and then adds them to the queue. Otherwise, I don't understand how the program would know what callback to run... – Liangtao Hu Jun 06 '23 at 19:39
  • Calling `then` method on a promise registers a fulfilment handler for that particular promise. Internally it might just be a list of function(s) to execute when the promise resolves. Each instance of promise has its own list. – Yousaf Jun 06 '23 at 19:41
  • Ok, I think I understand now. Promises are just a way to notify clearly which events should happen after an asynchronous event finishes. Just to make sure, this is how my testCode works: 1. Two Promises are added to the microTask Queue and each promise has a callback associated with it now 2. At the end of the current loop, all the microTasks are completed, setting up timers for our resolve callbacks 3. Once the timer completes, two callbacks are added to the main task queue. 4. The output is `2 1` – Liangtao Hu Jun 06 '23 at 19:52
  • @LiangtaoHu `Two Promises are added to the microTask Queue`, to be clear here, No. When the `resolve` is called, the Promise thenables are placed into the `microTask` queue. It's basically doing this internally on your thenables. -> `queueMicrotask(() => console.log("Run thenables"));` IOW: the resolves queues a microtasks to trigger the promises thenables. (note: it may be more than 1 thenable per promise). – Keith Jun 06 '23 at 20:02
  • I have added the detailed explanation of how your code executes – Yousaf Jun 06 '23 at 20:04
  • @Keith The callback passed to `then` are called "(promise) handlers" or "(promise) actions". A ["thenable"](https://stackoverflow.com/q/29435262/1048572) is something different. – Bergi Aug 22 '23 at 18:29