• MacroTask and microTask understanding
    • preface
    • case
    • Task queue
    • What exactly do macroTask and microTask refer to?
      • macroTask
      • microTask
    • Event loop
    • The application of microTask
      • The application of the Vue
      • Module application of Core-JS
    • Let’s do it one last time.
    • reference

preface

What is macroTask? What is microTask?

Before I read macroTask and microTask related literature, I have only had an impression of it, but I do not know its specific meaning.

React Scheduler Scheduler uses MessageChannel to trigger task execution.

React why don’t you call the method directly instead of using the postMessage method of the MessageChannel to trigger the call?

This triggered my thinking on this part, and I learned the mechanism of macroTask and microTask and my understanding of this mechanism.

This is conducive to dealing with the order we often encounter in interviews, the need to answer the interviewer given a section of JS code after the execution of the output log results;

React implements scheduling mechanisms for requestIdleCallback compatibility.

I believe that for front-end knowledge, this piece is a must to understand the content.

case

Here’s a look at some of the JavaScript event loops in action when processing tasks:

console.log(1);

setTimeout(function(){
  console.log(2);
}, 0);

console.log(3);
Copy the code

The output is 1, 3, and 2

The above example is easy to answer because setTimeout will enter the asynchronous task queue (although the delay time is set to 0), and the JavaScript main stack queue will output 1 and 3 from the top down. When the main stack queue is empty, it will exit the asynchronous task queue and enter the main stack queue. Because for the internal mechanics of JavaScript, it’s single threaded.

Let’s look at the second example:

console.log(1);

setTimeout(function(){
  console.log(2);
}, 0);

var res = Promise.resolve(6).then(function(value){
  console.log(3);
  return value
}).then(function(value){
  console.log(4);
  return value
});

res.then(function(value){
  console.log(value);
});

console.log(5);

Copy the code

The output is 1, 5, 3, 4, 6, and 2

The second case here, should be in the front-end interview, will often encounter this type of question, in fact, this question is very simple, of course, after the correct understanding of the asynchronous task queue macrotask and microtask, this type of question is very simple to answer. Promises, setTimeout, and THEN can be confusing if you don’t understand the execution mechanism of asynchronous queues.

In the second case above, the Promise asynchronous task takes precedence over the setTimeout delay of 0.

The reason is that the task queue is divided into macroTask and microTask, and the function of then method in Promise will be pushed into the microTask queue, while the setTimeout function will be pushed into the macroTask task queue.

MacroTask will only extract one execution per event loop, while microTask will continue to extract until the microTask queue is empty.

That is, if a microTask is pushed to execution, the next task in the queue will be called to execute after the main task is completed, until the task queue reaches the last task. The event loop pushes only one macroTask at a time, and the main thread checks the microTask queue and completes all tasks in it before executing the macroTask.

Task queue

Generally speaking, front-end people know that one of the characteristics of JavaScript is single-threaded, that is, you can only do one thing at a time. This design has to do with being a browser scripting language. JavaScript’s primary purpose is user interaction and dom manipulation, which makes it a single-threaded design that would otherwise introduce complex synchronization issues.

For example, if JavaScript is multithreaded, one thread will delete a node, and another thread will operate on that node, so it will be very complicated for the viewer to determine the order of that thread, and the viewer will not know which one to execute first.

JavaScript single-threaded means that tasks need to be queued, and if the previous task takes a long time, it blocks the execution of subsequent tasks. For this reason, JavaScript has synchronous and asynchronous tasks, both of which need to be executed in the main thread execution stack; Asynchronous tasks need to be queued in the Task queue. The operation mechanism is as follows:

  1. Synchronization tasks are executed on the main thread, forming an execution stack
  2. Asynchronous tasks, JavaScript will stack the task and queue it in an asynchronous queue
  3. After the task on the main thread is executed, the JavaScript will be pushed out of the asynchronous task queue via the Event loop, and the task will be pushed into the main thread queue to start execution

The JavaScript event mechanism is shown below:

What exactly do macroTask and microTask refer to?

MacroTask and microTask refer to macroTask and microTask. In asynchronous queue, asynchronous task is divided into macroTask and microTask.

MacroTask and microTask are asynchronous tasks, which are pushed into the asynchronous task queue and wait for the main thread to be pushed into the queue for execution.

macroTask

MacroTask on the browser side, which can be interpreted as the browser rendering the page after the task is completed and before the next macroTask execution begins.

Actions that trigger a macroTask include:

  • Script (whole code)

  • SetTimeout, setInterval, setImmediate(Runs the callback function immediately after the browser completes other actions (such as events and display updates) – links)

  • I/O and UI interaction events

  • PostMessage, MessageChannel

microTask

Microtasks can be understood as tasks that are executed immediately after the macroTask is executed and before the page renders.

The operations that trigger a microTask include:

  • Promise.then

  • MutationObserver(provides the ability to monitor changes made to the DOM tree – link)

  • Process. NextTick (Node environment)

Let’s move on to the second example above:

console.log(1);

setTimeout(function(){
  console.log(2);
}, 0);

var res = Promise.resolve(6).then(function(value){
  console.log(3);
  return value
}).then(function(value){
  console.log(4);
  return value
});

res.then(function(value){
  console.log(value);
});

console.log(5);
Copy the code

The results of the program are already known: 1, 5, 3, 4, 6, 2

How exactly is this output executed?

Based on the understanding of macroTask and microTask, we know which tasks are in these two queues respectively:

MacroTasks: [setTimeout callback]

MicroTasks: [setTimeout callback 1, setTimeout callback 2, setTimeout callback 3]

For macro tasks and microtasks, why does the output feel like the microtasks have higher priority? This is due to the way macro and micro tasks work in JavaScript:

  • Execute a macroTask (including the overall Script code) and fetch an asynchronous task from the task queue if the Js execution stack is free

  • Microtasks encountered during the execution of a macroTask (including overall Script code) are added to the Micro Task Queue; Add macroTask to Macro Task Queue

  • All microTasks in the Micro Task Queue are executed in order immediately after macroTask has finished executing (because microTasks are called after macroTask has finished executing and before the page is rendered). If a microTask is created in the process of executing it, it is added to the end of the queue and executed during this cycle.

  • After all microtasks are executed, the browser starts rendering and the GUI thread takes over

  • After rendering, take the macroTask from the Macro Task Queue and start executing

Event loop

In the case that the main thread execution stack is idle, reading tasks from the task queue into the execution stack for execution is a continuous loop, so it is also called Event loop.

Event loop is a specification for asynchronous JS implementation, which has different implementation mechanisms in different environments, such as browsers and NodeJS implementation mechanisms:

  1. The browser Event loop is implemented according to the HTML standard definition, and the specific implementation is left to each browser manufacturer

Here’s the Event loop in the browser environment:

  1. Select the latest macroTask to enter the Event Loop task queue according to the first-in, first-out principle. If not, go directly to the microTask in step 6

  2. Select the current Event loop task ready to enter the execution stack for execution

  3. Event loop The current task is pushed

  4. When the execution stack finishes running all tasks, the Event loop mechanism checks the asynchronous queue again to see if there are any tasks

  5. After a microTask is executed, the Event loop preferentially checks whether the microTask exists. If yes, the Event loop checks whether the microTask exists

  6. Push the microTask onto the stack again

  7. Update and render the UI

  8. Return to the first step

  1. The Event loop in NodeJS is based on libuv implementation

Based on the diagram above, Node.js works as follows.

(1) V8 engine parses JavaScript scripts.

(2) Parsed code, call Node API.

(3) Libuv library is responsible for the execution of Node API. It assigns different tasks to different threads, forming an Event Loop that asynchronously returns the execution results of the tasks to the V8 engine.

(4) The V8 engine returns the result to the user.

In addition to setTimeout and setInterval, Node.js provides two other “task queue” related methods: process.nextTick and setImmediate. They can help us deepen our understanding of “task queues”.

The process.nextTick method fires the callback function —- at the end of the current “execution stack” —- before the next Event Loop (the main thread reads the “task queue”). That is, the task it specifies always takes place before all asynchronous tasks. The setImmediate method adds an Event to the end of the current “task queue,” which means that the task it specifies is always executed on the next Event Loop, much like setTimeout(fn, 0).

The application of microTask

The application of the Vue

The application of microTask asynchronous task has been used in Vue, which has a clear explanation and explanation on the official website (link). The following is the official explanation and case of Vue:

Vue is executed asynchronously when updating the DOM. As long as it listens for data changes, Vue opens a queue and buffers all data changes that occur in the same event loop. If the same watcher is triggered more than once, it will only be pushed into the queue once. Then, during the next “tick” event loop, Vue flushes the queue and performs the actual work, scheduling the tasks through microTask and executing the caches (at which point the Dom tree is updated). Vue internally tries to use native Promise.then, MutationObserver, and setImmediate for asynchronous queues, and setTimeout(fn, 0) instead if the execution environment does not support it.

For example, when you set vm.someData = ‘new value’, the component does not immediately re-render. When the queue is refreshed, the component is updated in the next event loop “TICK”. In most cases we don’t need to worry about this process, but if you want to do something based on the updated DOM state, it can be tricky. While vue.js generally encourages developers to think in a “data-driven” way and avoid direct contact with the DOM, sometimes we have to. To wait for Vue to finish updating the DOM after the data changes, use vue.nexttick (callback) immediately after the data changes. This callback will be called after the DOM update is complete. Such as:

Vue.component('example', { template: '<span>{{ message }}</span>', data: function () { return { message: }}, methods: {updateMessage: Function () {this.message = 'updated' console.log(this.$el.textContent) // => 'unupdated' this.$nextTick(function () {this.message = 'updated' console.log(this. The console. The log (enclosing $el. TextContent) / / = > 'updated'}}}})Copy the code

In this official example, this.$nextTick will trigger a microTask that leaves calling this.$nextTick after the data assignment changes. The callback task is then placed behind the microTask queue in the same time loop as the Vue refresh queue that performs the actual work. This callback is executed after the Dom data is actually updated with the Vue and before the viewer renders. To get the updated value of this.$el.textContent

Because $nextTick() returns a Promise object, you can do the same thing using the new ES2017 async/await syntax:

methods: { updateMessage: Async function () {this.message = 'updated' console.log(this.$el.textContent) // => 'unupdated' await this.$nextTick() Console. log(this.$el.textContent) // => 'Updated'}}Copy the code

Module application of Core-JS

Reference source

module.exports = function () { var head, last, notify; var flush = function () { var parent, fn; if (isNode && (parent = process.domain)) parent.exit(); while (head) { fn = head.fn; head = head.next; try { fn(); } catch (e) { if (head) notify(); else last = undefined; throw e; } } last = undefined; if (parent) parent.enter(); }; // node.js if (isNode) {notify = function () {// node.js process.nexttick (flush); }; Browsers with MutationObserver} else if (Observer) {// MutationObserver mode var toggle = true; var node = document.createTextNode(''); new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new notify = function () { node.data = toggle = ! toggle; }; // environments with maybe non-completely correct, Resolve (); // An existentialPromise.resolve (); // An existentialPromise.resolve (); notify = function () { promise.then(flush); }; // for other environments - macrotask based on: // - setImmediate // - MessageChannel // - window.postMessag // - onreadystatechange // - setTimeout } else { notify = function () { // strange IE + webpack dev server bug - use .call(global) macrotask.call(global, flush); }; } return function (fn) { var task = { fn: fn, next: undefined }; if (last) last.next = task; if (! head) { head = task; notify(); } last = task; }; };Copy the code

Let’s do it one last time.

console.log(1);

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
}, 0);

new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
})

setTimeout(() => {
  console.log(6);
}, 0)

console.log(7);
Copy the code

This is a common topic in the interview question, through reading the above content, such a question, I believe you should be easy to solve, ha ha ~

Answer: 1, 4, 7, 5, 2, 3, 6

Resolution:

  1. First, 1, 4, and 7 are all tasks that are executed directly on the execution stack at the beginning
  2. MicroTask will be executed first, since 1, 4, and 7 are executed as a Macrotask-script, so output 5
  3. After the microTask queue is cleared, the next macroTask is executed, which prints 2 and passesPromise.resolve()Push a microTask
  4. After executing macroTask 2, microTask 3 will be executed, so 3 is output at this time
  5. Finally, the microTask queue is emptied and the next macroTask 6 is executed, at which point 6 is printed

reference

  • Vue asynchronously updates the queue
  • Talk about macrotasks and microtasks
  • The React source Scheduler
  • Concurrency model and event loop