skip to Main Content

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:

  • After a script execution: When a JavaScript call stack finishes processing, the event loop will first run any pending microtasks before handling anything else.
  • After each macro task: Whenever a task from the task queue (e.g., event handler from a click) finishes, another microtask checkpoint occurs, again processing any microtasks before moving to the next macro task.

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?

2

Answers


  1. The microtask that will log "promise!" will be executed right after the timer task scheduled from the Promise constructor got executed, but the two other timer tasks were scheduled before that one so they’re executed first. The fact there is a microtask queued from the Promise’s resolution doesn’t really matter here:

    setTimeout(() => {
        console.log('timeout1');
    }, 3000);
    
    setTimeout(() => {
        console.log('timeout2');
    }, 3000);
    
    let promise = new Promise(function (resolve, reject) {
        setTimeout(() => {
          resolve('promise!');
          // 'timeout3' will be logged before 'promise!' but after both other timeouts
          // because these got queued before us.
          console.log('timeout3'); 
        }, 3000);
    
    });
    
    promise.then(
        (result) => console.log(result),
        (err) => console.log(err)
    );
    Login or Signup to reply.
  2. 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);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search