preface

From the beginning to do the front end so far, have seen a lot of posts about JS running mechanism, read soon forget, or their reason again better

Use code words to make yourself more profound about the JS runtime mechanism (the content of the posts I have written with my heart will also be remembered)

Drop by for everyone to have a look (I am too difficult, late at night code words, repeatedly modify, say so much is to ask you to point a thumb-up in the look)

Referred to a lot of information (post), take its essence, go to its dross, are at the end of the article, can understand by oneself

It’s time for a big js wave

From zero to one hundred again to one, from many aspects to understand the running mechanism of JS, experience more profound, please read carefully

This article is roughly divided into the following steps to help us more clearly understand the JS operation mechanism from the wide to the deep

  • First we need to understand the concept of processes and threads
  • Second we need to know the browser process thread common sense
  • Then, through Event Loop, macroTask and microtask, see how several threads of the browser work together
  • And then use examples to confirm our guess
  • Finally, how NodeJS works

Soul asking

JS operating mechanism in the usual front-end interview, whether the pen test or interview question hit rate is very high

Speaking of how JS works, how much do you know

See this you may say: JS running mechanism, very simple, event loop, macro and micro tasks that thing

Yes, as a front end we all know that, but if the interview really comes to this place, can you really answer it (soul question 🤔️)

No matter how much you know about JS, here we don’t have to stop reading, suppose you are currently in an interview, the interviewer asked you to explain JS operation mechanism, think about your answer, with 20 seconds (20 seconds is a long time in the interview), and then take the answer and then continue to read, someone once said:Reading without thinking is simply killing time, this is very good (because I said it, skin 😄)

There are also a lot of students who have just started to contact JS will be task queue execution stack micro task macro task these lofty points of the place to do very ignorant

Now, let’s go through them in detail so you can see them clearly

Processes and threads

What is a process

As we all know, the CPU is the core of the computer and undertakes all the computing tasks

According to the official website, processes are the smallest unit of CPU allocation

It literally means an ongoing program, and I understand it as a task program that can run independently and has its own resource space

Processes include running programs and the memory and system resources used by the programs

CPU can have a lot of process, our each open a computer software will generate one or more of the process, why the computer running the software will be more CARDS, because CPU space allocation of resources to each process, but a CPU only have so many resources, points out, the more the more CARDS, between each process is independent of each other, when the CPU is running a process, While the other processes are not running, the CPU uses a time-slice rotation scheduling algorithm to run multiple processes simultaneously

What is a thread

Threads are the smallest unit of CPU scheduling

A thread is a program running unit based on a process. A thread is an execution flow in a program. A process can have multiple threads

Only one flow of execution in a process is called single-threaded, that is, when a program executes, the path of the program is arranged in sequential order, the first must be processed before the next can be executed

Multiple execution streams in a process are called multithreading, which means that multiple threads can run simultaneously in a program to perform different tasks. That is, a single program is allowed to create multiple threads of parallel execution to complete their respective tasks

The difference between processes and threads

A process is the smallest unit of resources allocated by the operating system, and a thread is the smallest unit of program execution

A process consists of one or more threads, which can be understood as different lines of code execution within a process

Processes are independent of each other, but threads in the same process share program memory (including code snippets, data sets, heaps, etc.) and process-level resources (such as open files and signals).

Scheduling and switching: Thread context switching is much faster than process context switching

Multiprocess and multithreading

Multi-process: Multi-process means that two or more processes are allowed to run at the same time on the same computer system. The benefits brought by multi-process are obvious. For example, people can open the editor to type codes while listening to songs in netease Cloud, and the editor and the process of netease Cloud will not interfere with each other

Multithreading: Multithreading means that a program contains multiple execution streams. That is, a program can run multiple threads simultaneously to perform different tasks. That is, a single program is allowed to create multiple threads of parallel execution to complete their respective tasks

Why is JS single threaded

The single thread of JS is related to its purpose. As a browser scripting language, JavaScript’s primary purpose is to interact with users and manipulate the DOM. This means that it has to be single-threaded, which can cause complex synchronization problems. For example, if there are two threads of JavaScript at the same time, one thread adds content to a DOM node, and the other thread removes that node, which thread should the browser use?

Some people say that JS has Worker threads. Yes, in order to make use of the computing power of multi-core CPU, 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 cannot operate DOM

So, this standard doesn’t change the nature of JavaScript being single-threaded

Now that we know about processes and threads, let’s look at browser parsing. There are some differences between browsers, but they’re pretty much the same. Let’s use Chrome, which has the largest market share, as an example

The browser

Browsers are multi-process

As a front-end, it is inevitable to deal with the browser, the browser is multi-process, take Chrome for example, every time we open a Tab page will produce a process, we use Chrome to open a lot of tabs, the computer will be more and more card, among other things, the first is very CPU consumption

Which processes are included in the browser

  • The Browser process

    • The browser’s main process (responsible for coordination and control), which has only one process
    • Responsible for browser interface display and user interaction. Forward, backward, etc
    • Responsible for page management, creating and destroying other processes
    • Draw a Bitmap in memory from the Renderer process onto the user interface
    • Network resource management, download, etc
  • Third-party plug-in processes

    • Each type of plug-in corresponds to a process that is created when the plug-in is used
  • GPU process

    • There is also only one process for 3D drawing and so on
  • Render process (heavy)

    • Known as the browser kernel (Renderer process, with multiple threads inside)
    • Each Tab page has a rendering process that does not affect each other
    • The main function is page rendering, script execution, event processing and so on

Why do browsers need multiple processes

Let’s assume that the browser is a single process, and one Tab page crashes, and it affects the entire browser, how bad the experience is

Similarly, a plugin crash can affect the entire browser

Of course, multiple processes have many other advantages, but more elaboration

There are many browser processes, and each process has many threads, which can take up memory

This also means that resources such as memory are expensive, which is a bit like trading space for time

This isn’t just to help us understand why Chrome freezes up when it runs for a long time

Renderer(heavy)

Page rendering, JS execution, event loop, are all performed within the renderer process, so we will focus on the renderer process

Renderers are multithreaded, so let’s take a look at some of the main threads used by renderers

The main thread of the Renderer process

GUI rendering thread

  • Responsible for rendering browser interfaces, parsing HTML, CSS, building DOM trees and RenderObject trees, layout and drawing, etc
    • Parsing HTML code (which is essentially a string) into browser-aware nodes generates a DOM Tree, or DOM Tree
    • Parsing CSS to generate CSSOM(CSS rule tree)
    • Combine DOM Tree and CSSOM to create Rendering Tree
  • When we change the color of some element or the background color, the page is repainted.
  • When we change the size of the element, the page Reflow.
  • The GUI thread executes when the page needs Repaing and Reflow, drawing the page
  • Reflow is more expensive than Repaint, so avoid Reflow and Repaint as much as possible
  • GUI rendering threads and JS engine threads are mutually exclusive
    • GUI threads are suspended when the JS engine is executing.
    • GUI updates are stored in a queue until the JS engine is idle and executed immediately

JS engine thread

  • The JS engine thread is the JS kernel and is responsible for processing Javascript scripts (such as the V8 engine)
  • The JS engine thread is responsible for parsing Javascript scripts and running code
  • The JS engine waits for tasks to arrive in the task queue and then processes them
    • Browsers can only have one JS engine thread running a JS program at a time, so JS is run single-threaded
    • Is there only one JS thread running on a Tab page (renderer process) at any one time
  • The GUI rendering thread is mutually exclusive with the JS engine thread, which blocks the GUI rendering thread
    • Is that we often encounter JS execution time is too long, resulting in incoherent page rendering, resulting in page rendering load blocking (is slow loading)
    • For example, when the browser renders<script>Tag, the GUI will stop rendering, and then the JS engine thread will start working, executing the js code inside. When the JS execution is complete, the JS engine thread will stop working, and the GUI will continue rendering the following content. So if the JS execution time is too long, it will cause the page to stall

Event trigger thread

  • Belongs to the browser, not the JS engine, controls the event loop, and manages a task queue.
  • When JS execution encounters event binding and some asynchronous operations (such as setTimeOut, but also other threads from the browser kernel, such as mouse click, AJAX asynchronous request, etc.), it will go to the event trigger thread to add the corresponding event to the corresponding thread (such as timer operation, then add the timer event to the timer thread). When the asynchronous events have a result, their callback operations are added to the event queue, waiting for the JS engine thread to become idle.
  • When the corresponding event meets the trigger condition is triggered, the thread will add the event to the end of the queue to be processed, waiting for the JS engine to process
  • Since JS is single-threaded, the events in the queue must be queued for processing by the JS engine

Timing trigger thread

  • setIntervalwithsetTimeoutThe thread
  • Browser timing counters are not counted by JavaScript engines (because JavaScript engines are single-threaded, blocking threads can affect timing accuracy)
  • By a separate thread to time and trigger timing (after the timing is finished, added to the event trigger thread event queue, waiting for the JS engine idle after execution), this thread is the timing trigger thread, also known as the timer thread
  • W3C specifies requirements in the HTML standardsetTimeoutIf the interval is less than 4ms, it is 4ms

Asynchronous HTTP request threads

  • After the XMLHttpRequest connects, the browser opens a new thread request
  • When a state change is detected, if a callback function is set, the asynchronous thread generates a state change event, which is then queued for execution by the JavaScript engine
  • In simple terms, when an ASYNCHRONOUS HTTP request is executed, the asynchronous request event is added to the asynchronous request thread, and the callback function is added to the event queue, waiting for the JS engine thread to execute

With that in mind, let’s move on to today’s topic

A preliminary study of Event Loop

The first thing to know is that JS is divided into synchronous and asynchronous tasks

Synchronization tasks are executed on the main thread (in this case, the JS engine thread) and form a stack of execution

Outside of the main thread, the event-triggering thread manages a task queue and puts an event callback into the task queue whenever the asynchronous task has a result

Once all synchronous tasks in the execution stack are completed (that is, the JS engine thread is idle), the system will read the task queue, add the runnable asynchronous tasks (event callback in the task queue, as long as there is event callback in the task queue, it can be executed) to the execution stack, and start execution

Let’s look at a simple piece of code

let setTimeoutCallBack = function() {
  console.log('I'm a timer callback');
};
let httpCallback = function() {
  console.log('I'm an HTTP request callback');
}

// Synchronize tasks
console.log('I'm synchronous task 1');

// Asynchronous scheduled task
setTimeout(setTimeoutCallBack,1000);

// Asynchronous HTTP request task
ajax.get('/info',httpCallback);

// Synchronize tasks
console.log('I'm synchronous task 2');
Copy the code

The above code execution process

JS is executed sequentially from top to bottom, which can be understood as the execution environment of this code is the main thread, that is, the current execution stack

First, execute console.log(‘ I am synchronization task 1’)

Then, when setTimeout is executed, the timer thread is handed the setTimeoutCallBack callback to the event trigger thread. After 1s, the event-triggering thread receives the setTimeoutCallBack callback and adds it to the event queue managed by the event-triggering thread for execution

The httpCallback callback is then handed over to the asynchronous HTTP request thread to send the network request. Upon success, the httpCallback callback is handed over to the event-triggering thread, which receives the httpCallback callback and adds it to the event-triggering thread’s event queue

Then execute console.log(‘ I’m sync task 2’)

At this point, the main thread execution stack has been completed JS engine thread is idle, started asking, thread to trigger events ask events trigger the event queue of the thread if there is a need to perform the callback function, if there is a callback event from the event queue to join the execution stack, began to implement the callback, if there is no callback event queue, JS engine threads will always ask, Until there is

At this point, we see that all threads on the browser are working singly and independently, very much in line with the single principle

The timer trigger thread only manages the timer and only cares about the timer and doesn’t care about the result. When the timer ends, it throws the callback to the event trigger thread

The asynchronous HTTP request thread only manages the HTTP request and does not care about the result. The request terminates and throws the callback to the event-triggering thread

The event-triggering thread only cares about asynchronous callbacks to the event queue

However, our JS engine thread will only execute the events in the execution stack. After the execution of the code in the execution stack is completed, it will read the events in the Event queue and add them to the execution stack to continue the execution. In this way, the repetition and repetition is the so-called Event Loop.

The illustration

First, the execution stack starts sequential execution

The event is called back to the task queue of the event-triggered thread for execution, and the synchronization continues

Empty execution stack, asking if there are event callbacks in the task queue

If there is an event callback in the task queue, the callback is added to the end of the stack to continue execution from the first step

If there is no event callback in the task queue, the query is continuously initiated

Macrotask & MicroTask

Macro task (macrotask)

In ECMAScript, MacroTask is also referred to as task

We can think of the code executed on each stack as a macro task (including fetching one event callback from the event queue and placing it on the execution stack at a time), and each macro task will be executed from start to finish and nothing else will be done

As the JS engine thread and GUI rendering thread are mutually exclusive, in order to make macro tasks and DOM tasks orderly, the browser will start rendering the page after the execution of one macro task and before the execution of the next macro task

Macro Tasks -> GUI Render -> Macro Tasks ->...Copy the code

Common macro tasks

  • The main block of code
  • setTimeout
  • setInterval
  • setImmediate ()-Node
  • RequestAnimationFrame ()- Browser

Micro tasks (microtask)

ES6 introduced the Promise standard, and the browser implementation added the concept of microtasks, also known as Jobs in ECMAScript

We already know that after the macro task is finished, rendering is performed, and then the next macro task is executed, while the micro task can be understood as a task that is executed immediately after the current macro task is executed

When a macro task completes, all microtasks generated during execution are completed before rendering

Macro Tasks -> Micro Tasks -> GUI Rendering -> Macro tasks ->...Copy the code

Common microtasks

  • process.nextTick ()-Node
  • Promise.then()
  • catch
  • finally
  • Object.observe
  • MutationObserver

Simply distinguish between macro tasks and micro tasks

You may not be too clear about the macro and micro tasks described above. That’s ok. Read on and remember the common macro and micro tasks

Let’s look at a few examples, which are from the idea of gold Mining cloud in the article reference link [14], by rendering the background color to distinguish macro tasks and micro tasks, very intuitive, I think it is very interesting, so here also use this example

Find a blank page and type the following code in console

document.body.style = 'background:black';
document.body.style = 'background:red';
document.body.style = 'background:blue';
document.body.style = 'background:pink';
Copy the code

We see the above diagram directly apply colours to a drawing the pink background, according to the above telling the browser will be performed in a macro task, then the current execution stack of all tasks, and then handed over to the GUI rendering, the above four lines of code are macro tasks belong to the same time, to perform rendering, completing execution of the rendering GUI thread will merge all UI changes to optimize, so visually, You just see the page turn pink

Then see again

document.body.style = 'background:blue';
setTimeout((a)= >{
    document.body.style = 'background:black'
},200)
Copy the code

In the code above, the page will be blue first, then black background, the page says 200 milliseconds, you can think of it as 0 milliseconds, because 0 milliseconds because browser rendering is too fast, recording is not easy, I don’t have a slow recording tool, you can test yourself, the result is the same, The safest way to do this is to create an index. HTML file, insert the js script in the file, and then open the browser. It is best to use the Performance function in the console to view frame by frame.

Getting back to the point, the reason for the blue block is that the code above belongs to two macro tasks. The first macro task executes the code to turn the background blue, then triggers the render to turn the page blue, and then triggers the second macro task to turn the background black

Look at

document.body.style = 'background:blue'
console.log(1);
Promise.resolve().then((a)= >{
    console.log(2);
    document.body.style = 'background:pink'
});
console.log(3);
Copy the code

The console prints 1, 3, and 2 because the callback for the Promise object’s then method executes asynchronously, so 2 prints last

The background color of the page went straight to pink, without going through the blue phase, because we set the background to blue in the macro task, but performed the microtask before rendering, which turned the background to pink before rendering

Pay attention to micro tasks and macro tasks

  • The browser executes a macro task, then executes the microtask generated by the current stack, then renders, and then executes the next macro task
  • Microtasks and macro tasks are not in the same task queue, not in the same task queue
    • For example,setTimeoutIs a macro task whose event callback is in the macro task queue,Promise.then()Is a microtask whose event callback is in the microtask queue, which is not a task queue
    • In Chrome, for example, everything about rendering is performed in the rendering process. Tasks in the rendering process (DOM tree building, JS parsing… , etc.) to the main thread to perform the tasks will be performed in the main thread, and the browser maintains a set of event loop mechanism, the main thread will be put in the message queue task execution, on the main thread will cycle the message queue, and removed from the head of task execution, if produced during the implementation of other tasks need to be the main thread, Other threads in the renderer process stuff the task to the end of the message queue, where tasks are macro tasks
    • How do microtasks come about? When executing the script of the script, js engine will create an implementation for the global context, in the execution context maintains a task queue, when confronted with a task micro, will put micro task callback queue, when all the js code execution and exit in the global context before the engine will go to check the queue, a callback is executed, This is why micro tasks are earlier than macro tasks. It is also commonly said that each macro task has a micro task queue (timer is a browser API, so timer is a macro task, and timer encountered in JS will also be put into the browser queue).

At this point, you may still be confused, no matter, please continue to read

Illustrates macro and micro tasks

First, execute a macro task, and determine whether there are microtasks after execution

With microtasks, perform all microtasks first and then render, without microtasks, render directly

Then proceed to the next macro task

Illustrate the complete Event Loop

First, when the overall script(as the first macro task) starts executing, all the code is divided into synchronous tasks and asynchronous tasks

The synchronization tasks are directly executed on the main thread

Asynchronous tasks are subdivided into macro tasks and micro tasks

The macro task enters the Event Table and registers the callback function in the Event Table, which will be moved to the Event Queue whenever the specified Event completes

The microtask will also go to another Event Table and register a callback function in it, which will be moved to the Event Queue whenever the specified Event completes

When tasks in the main thread are completed and the main thread is empty, the Event Queue of microtasks will be checked. If there are tasks, they will all be executed. If there are no tasks, the next macro task will be executed

This process is repeated over and over again. This is called an Event Loop, a relatively complete Event Loop

On the Promise

New Promise(() => {}).then(), let’s look at this Promise code

The previous new Promise() part is a constructor, which is a synchronization task

The.then() is an asynchronous microtask, which is important

new Promise((resolve) = > {
	console.log(1)
  resolve()
}).then((a)= >{
	console.log(2)})console.log(3)
Copy the code

The code above prints 1, 3, 2

About async/await functions

Async /await is essentially a wrapper around promises, which are a type of microtask

So using the await keyword has similar effects as promise.then

setTimeout((a)= > console.log(4))

async function test() {
  console.log(1)
  await Promise.resolve()
  console.log(3)
}

test()

console.log(2)
Copy the code

The above code prints 1, 2, 3, and 4

The code after await equals asynchrony with promise.then

For chestnut confirm

Let me first give you a more intuitive GIF

The reason for putting this GIF is to recommend this good article to everyone. The GIF recording screen is from the reference link [1].

I highly recommend you to have a look at this post, very nice, vivid and intuitive step animation, if you have time to experience it yourself

But before you read this post you should understand the operation mechanism to make it easier to read

Next this comes from the Internet to find a relatively simple interview questions, for the output results

function test() {
  console.log(1)
  setTimeout(function () { 	// timer1
    console.log(2)},1000)
}

test();

setTimeout(function () { 		// timer2
  console.log(3)})new Promise(function (resolve) {
  console.log(4)
  setTimeout(function () { 	// timer3
    console.log(5)},100)
  resolve()
}).then(function () {
  setTimeout(function () { 	// timer4
    console.log(6)},0)
  console.log(7)})console.log(8)
Copy the code

Combine our above JS operation mechanism to look at this problem is much simpler and clearer

JS is executed sequentially from top to bottom

Execute to test(), the test method is synchronized, execute directly, console.log(1) prints 1

In the test method, setTimeout is an asynchronous macro task, and the callback is called timer1 and put into the macro task queue

Then execute the test method with a setTimeout for the asynchronous macro task, call it timer2 and put it in the macro task queue

And then we execute the promise, new promise is the synchronization task, execute it directly, print 4

The setTimeout in new Promise is an asynchronous macro task, and the callback is called timer3 and put into the macro task queue

Promise.then is the microtask, put on the microtask queue

Console. log(8) is a synchronization task that is executed directly and prints 8

When the main thread task completes, check for promise. then in the microtask queue

SetTimeout is an asynchronous macro task. Call it timer4 and put it in the macro task queue

Console. log(7) in the microtask queue is a synchronization task, executed directly, printing 7

The microtask completes, and the first loop ends

Check the macro task Queue, which contains timer1, Timer2, timer3, timer4, four timer macro tasks, according to the timer delay time can be executed in order, namely, Event Queue: Timer2, Timer4, Timer3, and Timer1 are placed at the end of the execution stack.

Run timer2, console.log(3) for synchronization and print 3

Check that there are no microtasks. The second Event Loop ends

Run timer4, console.log(6) for synchronization and print 6

Check that there are no microtasks. The third Event Loop ends

Perform timer3, console.log(5) synchronization and print 5

Check that there are no microtasks. The fourth Event Loop ends

Perform timer1, console.log(2) synchronization and print 2

Check that there are no microtasks or macro tasks. The fifth Event Loop ends

Results: 1,4,8,7,3,6,5,2

Mention the running mechanism in NodeJS

Everything above is EventLoop for the browser

The JavaScript environment in NodeJS is V8 and single-threaded, but there are some differences in how it behaves in browsers

The difference between NodeJS and browsers is that there are several types of macro tasks in NodeJS, which have different task queues, and different task queues have different order, while microtasks are interspersed between each type of macro tasks

In the Node environment, process.nextTick has a higher priority than Promise, which can be simply interpreted as that the nextTickQueue part in the microtask queue will be executed first after the macro task ends, and then the Promise part in the microtask will be executed

The image above is from NodeJS

As you can see above, there are several types of NodeJS macro tasks, so we’ll just cover the basics and not explain them in detail

NodeJS ‘Event Loop is relatively cumbersome

Node executes all macroTasks of type Timers, then executes all microtasks (except NextTick) into the poll phase, executing almost all macroTasks, Then execute all microTasks then execute all MacroTasks of type Check, then execute all MicroTasks then execute all MacroTasks of type close callbacks, Then execute all microtasks to this point, complete one Tick and return to timers stage... And so on and so on...Copy the code

The Event Loop in a browser is easier to understand

Execute one MacroTask, then all microTasks, then one MacroTask, then all MicroTasks... And so on and so on...Copy the code

NodeJS Event loops are much more complex to interpret than browsers, so it’s only a comparison

The last

The above flow chart is drawn by myself, so it is a little low, forgive me

The level is limited, welcome to point wrong

Code word is not easy, after reading to help you please like, have questions please comment

After reading this post, I recommend reading the article “In-depth Understanding of asynchronous solutions for core JS”, which will have a deeper understanding of JS asynchronous programming

Recently picked up a frozen public number, and made it again

Welcome everyone to pay attention to [not serious front end], add me, add group, or take some information can, from time to time to send some quality original

Author: isboyjc

E-mail: 214930661 @qq.com

GitHub: Github.com/isboyjc

reference

  1. Queues and Schedules – Tasks, MicroTasks, Queues and Schedules

  2. Talk about JavaScript and browsers – engines and threads

  3. Front-end Digest: An in-depth look at how browsers work behind the scenes

  4. Browser processes? Thread? Silly silly can’t tell!

  5. From entering CNblogs.com to the front page of the blog park completely show what happened

  6. Front End must-read: The inner workings of browsers

  7. What is an Event Loop?

  8. More on the Event Loop

  9. The difference between single thread and multi-thread

  10. Browser process/thread model and JS runtime mechanism

  11. Operating Mechanism of the browser – 2. What processes does the browser contain?

  12. Does JS have to be at the bottom of the Body? Let’s talk about browser rendering

  13. From browser multi process to JS single thread, JS running mechanism is the most comprehensive combing

  14. “Front-end advancement” from multi-threading to Event Loop comprehensive combing

  15. Js basic knowledge (4) – Js operating principle and mechanism

  16. This time, thoroughly understand the JavaScript execution mechanism

  17. Front-end performance optimization: Detail rearrangement and redrawing of browser rendering

  18. 10 minutes to understand the browser rendering process and optimization