JavaScript the Fun Part


effective-javascript-2

main-task-micro-task-macro-task


We know or at least hear that JavaScript is a single threaded engine.

Also we hear about asynchronous things in JavaScript.

If it seems wired to you, you are right.

How a single threaded engine does asynchronous operations? We will understand it here.

Main Task

Main tasks are those the engine has to run and they are pure JavaScript code.

Which means no API calls to the DOM, no event listeners, etc.

These tasks should be done first, and therefore main tasks priority is 1. No other kinds of tasks will be run, unless there is no main task.

For example if we add an infinite while loop in our JS file when loading a page, it is considered as a main task with priority of 1 and nothing else will be run till finishing this task.

// infinite while loop
while( true ){
    console.log( "infinite loop" );
}

When we encounter an infinite loop like above, you can test it that page responsiveness will be gone. Why?

Very simple. What is the purpose of using JavaScript in a web browser? Yes, manipulating the DOM.

So when the JavaScript engine is busy with our infinite while loop, it implicitly means hey browser, do not render the page, I am busy with it.

In fact no other tasks, button clicks, scrolling, animations, etc will work. Everything stops because the JS engine is 100% busy with the infinite while loop.

It is not happen for everyone. If we have a powerful CPU then we have a kind of response of the page not complete freezing.

But with a weak CPU we may encounter a complete freeze of the page.

Real Scenario

Imagine you have a heavy animation on you home page. And it is done using JavaScript, so it is run as main task.

Now if we add an event listener on scroll event of the page, interesting things happen.

Since JS Call Stack is busy running animation and meanwhile we are scrolling, many scroll events are queued in event queue..

Result? Very simple. Those events (scrolling) do not get change to be run since JavaScript engine is busy with animation.

Micro Task

These kind of tasks are related to Web API, e.g. AJAX calls, event listeners, etc.

The JavaScript engine does not know how to do them. We will code these events in our JS file, but they are delivered to another threaded managed by the browser.

For example for an AJAX request, we cannot predict when it will be finished. So it does not make sense to keep the engine busy till AJAX does its tasks.

The proper way of doing an AJAX request is to hand it to another thread, and that thread does the task, and gives the result back to us.

We already did this. How? Callback functions.

Macro Task

These tasks are like Micro ones, but they have lower priority.

So the order of priority is

  1. Main tasks
  2. Micro tasks
  3. Macro tasks

Example

Notice that the order is NOT important, the priority is important.

Test this with Node.js, browser will give you error for empty for loop.
const log = console.log.bind( console );
log( "............ start of the main task ............" );

// micro task, priority 2
async function micro(){
    // break the flow and go to the event queue
    await null;
    let index = 0;
    for( ; index < 100000000; ++index );
    log( "micro index:", index );
}
// go to event queue
micro();

// macro priority 3
function macro(){
    let index = 0;
    for( ; index < 100000000; ++index );
    log( "macro index:", index );
}
// go the event queue 
setTimeout( macro, 0 );

// main task, priority 1
// DO IT RIGHT NOW
let index = 0;
for( ; index < 100000000; ++index );
log( "main index:", index );

log( "............. end of the main task ............." );


// output
............ start of the main task ............
main index: 100000000
............. end of the main task .............
micro index: 100000000
macro index: 100000000

What happens here? Simple.

First micro() function is run; since does not have priority is separated from main task and added to event queue for alter execution.

Second, our macro() function is part of the main task, but running it with setTimeout() will reduce the priority of it, and also will added to the event queue for later execution.

Third the third for loop is part of the main task and should be run immediately and will.

Finally JavaScript engine after finishing all the main tasks, will be idle.

But!

Since we have two other tasks in the event queue, event loop will deliver the micro task to JS engine.

Do we have anything else? Yes, our macro task, then delivered to JS engine.

What are event loop, and event queue? We will see it in next post.


Update: Sat Sep 21 2019 12:26:52 GMT+0430 (Iran Daylight Time)