Macrotasks and microtasks in javascript, macrotasks and microtasks in js

Hello everyone, let me share with you the macro tasks and micro tasks of JavaScript. Many people still don’t know this. Let’s explain it in detail below. Now let’s take a look!

The first time I came into contact with the micro-tasks and macro-tasks of js was during an interview. I was completely confused. Here are the interview questions for everyone to have a sneak peek at:

console.log(' start');
setTimeout(function(){
    console.log('setTimeout');
},0);
new Promise(function(resolve){
    console.log('promise1');
    resolve();
    console.log('promise2');
}).then(function(){
    console.log('promise then');
});
console.log(' end');

In order to figure out what is going on, I have been searching for a long time but I have not found the answer I want. Now I have compiled the results of my query as follows. Next, we will fundamentally explain the microtasks and macrotasks of JS in turn.

JS execution mechanism

Java is a single-threaded language, and Web-Worker was proposed in the latest HTML5, but the core of Java being single-threaded has not changed. Therefore, all Java versions of “multi-threading” are rewritten using GPT simulated by a single thread.

Multi-threading: A program can do several things at the same time.
Single-threaded: A program can only do one thing at a time.

1. Why is JS single-threaded?

One of the major features of the JavaScript language is single-threading, which means that it can only do one thing at a time. So why can’t JavaScript have multiple threads? This can improve efficiency.

JavaScript is single-threaded, depending on its purpose. As a browser scripting language, JavaScript’s main purpose is to interact with users and manipulate the DOM. This determines that it can only be single-threaded, otherwise it will cause very complex synchronization problems. For example, suppose JavaScript has two threads at the same time. One thread adds content to a certain DOM node, and the other thread deletes the node. In this case, which thread should the browser use?
?
Therefore, in order to avoid complexity, JavaScript has been single-threaded since its birth. This has become a core feature of the language and will not change in the future.

In order to take advantage of the computing power of multi-core CPUs, HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and must not operate the DOM. Therefore, this new standard does not change the single-threaded nature of JavaScript.

2. Why does JS need to be asynchronous?

If there is no asynchronous in JS, it can only be executed from top to bottom. If the parsing time of the previous line is very long, the following code will be blocked. For users, blocking means “stuck”, which leads to a poor user experience.

3. How does JS single-thread achieve asynchronousness?

Since JS is single-threaded and can only be executed on one thread, how is it achieved asynchronously?
?
It is through the event loop. If you understand the event loop mechanism, you will understand the execution mechanism of JS. Before understanding the event loop mechanism, let’s first take a look at the task queue.

Task Queue

“Task queue” is an event queue (can also be understood as a message queue). When the IO device completes a task, an event is added to the “task queue”, indicating that the related asynchronous tasks can enter “execution” Stack”. The main thread reads the “task queue”, which means reading the events in it.
?
The events in the “Task Queue”, in addition to IO device events, also include some user-generated events (such as mouse clicks, page scrolling, etc.). As long as the callback function is specified, these events will enter the “task queue” when they occur, waiting for the main thread to read.

“Task queue” is a first-in, first-out data structure. The events ranked first are read by the main thread first. The reading process of the main thread is basically automatic. As soon as the execution stack is cleared, the first event on the “task queue” will automatically enter the main thread. However, due to the “timer” function mentioned later, the main thread must first check the execution time. Certain events can only return to the main thread after the specified time.

To read an asynchronous task, first put the asynchronous task into the event table (Event table). When the asynchronous task placed in the event table completes something or meets certain conditions (such as the setTimeout event arrives, the mouse clicks , the data file is obtained), these asynchronous tasks are pushed into the event queue (Event Queue). The asynchronous tasks at this time are the asynchronous tasks that can only be read when the execution stack is idle.

4. Java execution mechanism

The main thread reads events from the “task queue”. This process is cyclic, so the entire operating mechanism is also called Event Loop.
Event Loop is the execution mechanism of java

Synchronous tasks and asynchronous tasks

1. Shortcomings of single thread

Single thread means that all tasks need to be queued, and the next task will be executed only after the previous task is completed. A simple understanding is to queue up one by one. If the previous task takes a long time, the next task will have to wait. If the queue is due to a large amount of calculation and the CPU is too busy, forget it, but many times the CPU is idle because the IO devices (input and output devices) are very slow (for example, Ajax operations read data from the network) and have to Wait for the results to come out before proceeding.

2. Synchronization and asynchronousness solve the shortcomings of single thread

The designers of the JavaScript language realized that at this time, the main thread can completely ignore the IO device, suspend the waiting tasks, and run the later tasks first. Wait until the IO device returns the result, then go back and continue executing the suspended task.

Therefore, all tasks can be divided into two types, one is synchronous task (synchronous) and the other is asynchronous task (asynchronous).

Synchronous and asynchronous tasks enter different execution “places” respectively, synchronously entering the main thread, and asynchronously entering the Event Table and registering functions. When the specified thing is completed, the Event Table will move this function into the Event Queue. When the tasks in the main thread are empty after execution, the corresponding function will be read from the Event Queue and executed in the main thread. The above process will be repeated continuously, which is often called Event Loop.

Synchronous tasks refer to tasks queued for execution on the main thread. The next task can only be executed after the previous task is executed;
Asynchronous tasks refer to tasks that do not enter the main thread but enter the “task queue” (task queue). Only the “task queue” notifies the main thread that an asynchronous task can be executed. , the task will enter the main thread for execution.

Macro tasks and micro tasks

Next comes the macro tasks and micro tasks we are going to talk about, both of which are asynchronous tasks.
The main difference lies in their execution order, the direction and value of the Event Loop.

1. Macro-task

Macro tasks refer to tasks initiated by the host (browser/node).
include:

  • Sync (whole code)
  • Timer
  • Event Binding
  • ajax
  • Callback function
  • FS in Node can perform asynchronous I/O operations
  • UI rendering

2. Micro-task

Microtasks refer to tasks initiated by the js engine.
include:

  • process.nextTick: The API implemented in node puts the current task on the main stack for last execution. When the main stack is finished executing, nextTick is executed first, and then found in the waiting queue.
  • Promise(async/await): Promise is not completely synchronous. It is a synchronous task in promise. When executing resolve or reject callback, it is an asynchronous operation, and then/catch, etc. will be placed first. to the microtask queue. When the main stack is completed, the resolve/reject method will be called again.
  • MutationObserver: Create and return a new MutationObserver which will be called when the specified DOM changes.

3. Mechanism: Microtasks are greater than macrotasks

JS asynchronous has a mechanism, that is, when encountering a macro task, the macro task is executed first, and the macro task is put into the event queue, and then the micro task is executed, and the micro task is put into the event queue. The most annoying thing is that these two queues are not one queue. When you take it out, first get the callback function from the microtask, and then get the callback function of the macrotask from the queue of the macrotask. When I saw this, I was convinced that there was such a naughty operation.

Application

Now let’s go back and look at the interview questions listed at the beginning of the article. The order of execution is as follows:

1. First, the JavaScript engine will execute a macro task. Note that this macro task generally refers to the main code itself, which is the current synchronization code.
2. If a microtask is encountered during execution, add it to the microtask task queue.
3. After the macro task execution is completed, the micro tasks in the current micro task queue are immediately executed until the micro task queue is cleared.
4. After the microtask execution is completed, start executing the next macrotask.
This cycle repeats until the macro tasks and micro tasks are cleared.

Specifically for this question, the order of execution is:

First execute a macro task, that is, all synchronization codes, and print out:
start
promise1
promise2
end
In the first step, the macro task execution is completed. Check the micro task queue and find that there is: Promise’s then callback function in the micro task queue. The micro task queue is executed immediately. Print out: promise then

Then execute the next macro task, execute the setTimeout callback function, and print out: setTimeout.

Also note in this question: the callback functions of setTimeout and setInterval are macro tasks, but the setTimeout and setInterval in the question themselves belong to the main code;

Similarly, although the two lines that print promise1 and promise2 are in the callback function of Promise, this function is executed immediately and synchronously, so it also belongs to the main code.