javascriptasynchronouspromisesettimeoutevent-loop

Details regarding micro-task queue checkpoints in Javascript


Based on this accepted answer regarding micro-task queue checkpoints in JS and looking up the concept more, I have come to understand the following:

Micro-task queue checkpoints happen at these points in the event loop:

To understand this via an example, I tried the following code example:

setTimeout(() => {
    console.log('timeout1');
}, 3000);

setTimeout(() => {
    console.log('timeout2');
}, 3000);

let promise = new Promise(function (resolve, reject) {
    setTimeout(() => resolve('promise!'), 3000);
});

promise.then(
    (result) => console.log(result),
    (err) => console.log(err)
);

According to the concept of event-loops in JS, setTimeouts functions are pushed to the macro-task queue after the designated time passes. Thus, all three setTimeout tasks should be pushed on to the macro-task queue after 3 seconds have elapsed. On the other hand, the function passed to the .then() of the promise should be pushed to the micro-task queue. For the above code, at the beginning of a subsequent cycle, the first setTimeout task that was pushed to the macro-task queue should be run to print 'timeout1'.

According to the micro-task queue checkpoint concept that I provided above, thereafter a checkpoint should occur and the print function in the micro-task queue should print 'promise!'.

Finally, the second macro-task on the macro-task queue 'timeout2' should be printed.

However, running the code on the console of Chrome and Firefox (latest versions), and via Node v16.14.0 on my machine, I find the order of printing to be:

timeout1
timeout2
promise!

Is micro-task queue checkpoint behavior not guaranteed? If it is, am I mistaken somewhere else in my evaluation?


Solution

  • Thus, all three setTimeout tasks should be pushed on to the macro-task queue after 3 seconds have elapsed. On the other hand, the function passed to the .then() of the promise should be pushed to the micro-task queue.

    No, that does not happen until the promise is actually fulfilled, during the execution of the third timer macro task.

    You get a better understanding of the order by fulfilling the promise from the second timer:

    setTimeout(() => {
        console.log('timeout1');
    }, 1000);
    
    new Promise(function (resolve, reject) {
        setTimeout(() => {
            console.log('timeout2');
            resolve('promise from timeout2');
        }, 1000);
    }).then(
        (result) => console.log(result),
        (err) => console.log(err)
    );
    
    setTimeout(() => {
        console.log('timeout3');
    }, 1000);