Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I am confused between JavaScript macro and micro tasks priority

Tags:

javascript

I was reading about micro and macro tasks in the JavaScript stack. I wrote this code:

Promise.resolve().then(function () {
      setTimeout(function () {
        console.log('from promise one');
      }, 0);
    }).then(() => {
      console.log('from promise two');
    });

    setTimeout(function () {
      console.log('from timeout');
    }, 0);

But I realized that from timeout shows faster than from promise one in the console...

As I understood, Promise. then() is a microtask and executes before macro task which from timeout is a microtask here... but why does execute the timeout first then Promise. then?

like image 983
Martin Rützy Avatar asked Sep 15 '25 08:09

Martin Rützy


1 Answers

Important things to know:

  1. setTimeout with a timeout of 0 will run the function at the beginning of the next event loop.
  2. The callbacks in Promise.resolve.then() are microtasks, and will be run after all of the macrotasks in the current iteration of the event loop have completed.

Here's a full explanation:

// The promise.resolve() runs first.
Promise.resolve()
    // Because the function calling the setTimeout is within a .then(), it will
    // be called at the END of the CURRENT iteration of the event look.
    .then(function () {
        // The callback inside this setTimeout will be run at the beginning of the
        // next event loop; however it will run after the "from timeout" log, because
        // the setTimeout is called AFTER the one at the very bottom of the file.
        setTimeout(function () {
            console.log('from promise one');
        }, 0);
    })
    .then(() => {
        // This log will occur first. No other logs will happen on the beginning of the
        // first iteration of the event loop, because everything is being called as
        // macro tasks except for this one.
        console.log('from promise two');
    });

// This setTimeout call runs before the other code above runs. That's because
// it is being called as a macro task for the current iteration of the event
// loop. The callback inside of it, however, will be called at the BEGINNING of
// the NEXT event loop.
setTimeout(function () {
    console.log('from timeout');
}, 0);

Quick rundown of the order at which things happen with the code above:

FIRST ITERATION OF THE EVENT LOOP:

  1. Promise.resolve() is called
  2. The setTimeout at the bottom of the file is called. Queues "from timeout" to be logged at the beginning of the next iteration of the loop.

- all macro tasks now finished. moving onto micro tasks -

  1. First .then() callback is called, and the setTimeout within queues up the "from promise one" log to be ran at the beginning of the next iteration of the event loop.
  2. Second .then() callback is called, and "from promise two" is logged.

SECOND ITERATION OF THE EVENT LOOP:

  1. "from timeout" is logged first, because it was the first macro task to be queued during the previous event loop iteration.
  2. "from promise one" is logged.

Output:

from promise two
from timeout
from promise one

Check out this short video for a succinct explanation of the event loop, microtasks and macrotasks, and asynchronous programming in JavaScript.

like image 65
mstephen19 Avatar answered Sep 17 '25 23:09

mstephen19