The more you know, the more you don’t know, the more like and see, the hand left fragrance, and have glory
primers
In almost every JS related book, it will be said that JS is single threaded, JS is through the Event queue (Event Loop) to achieve asynchronous callback. For many beginners of JS, it is not clear why single-threaded JS has asynchronous capability, so I try to explain this problem from the perspective of process and thread.
CPU
At the heart of a computer is the CPU, which does all the computing.
It’s like a factory, running all the time.
process
Suppose the plant has limited power to one plant at a time. In other words, when one workshop is open, all the other workshops have to shut down. The idea behind this is that a single CPU can only run one task at a time.
A process is like a factory floor; it represents the individual tasks that the CPU can handle. Processes are independent of each other. At any given time, the CPU is always running one process and the other processes are not running. The CPU uses the time slice rotation scheduling algorithm to run multiple processes simultaneously.
thread
In a workshop, there can be many workers, sharing all the resources of the workshop, they work together to complete a task.
Threads are like workers on the shop floor. A process can contain multiple threads that share process resources.
Relationships between cpus, processes, and threads
From the above we have a brief understanding of CPU, process, thread, a brief summary.
process
Is the smallest unit of CPU resources allocated (is the smallest unit that can hold resources and run independently)thread
Is the minimum unit of CPU scheduling (a thread is a unit of program running at one time based on a process, a process can have multiple threads).- different
process
They can also communicate with each other, but at a higher cost Single thread
withmultithreading
Both are referred to in aprocess
Inside the single and many
Browsers are multi-process
We have known the relationship between CPU, process, thread, for the computer, each application is a process, and each application will have a lot of function modules, these function modules are actually through the child process to achieve. The application can be said to be multiprocess for the way this seed process extends.
As for the browser, the browser is multi-process. I opened multiple tabs in Chrome and then opened the Windows Control Manager:
In the image above, you can see a Chrome browser launching several processes.
To summarize:
- Browsers are multi-process
- Each Tab page is a separate process
Which processes are included in the browser
- The main process
- Coordinate control of other child processes (creation, destruction)
- Browser interface display, user interaction, forward, backward, favorites
- Draws the in-memory Bitmap obtained by the rendering process to the user interface
- Handle invisible operations, network requests, file access, etc
- Third-party plug-in process
- Each type of plug-in corresponds to a process that is created only when the plug-in is in use
- GPU process
- Used for 3D rendering etc
Rendering process
That’s what we’re talking aboutBrowser kernel
- Responsible for page rendering, script execution, event handling, etc
- One render process per TAB page
So with so many processes in the browser, what’s the most important for normal front-end operations?
The answer is the rendering process, also known as the browser kernel
Browser kernel (rendering process)
As we know from the previous section, processes and threads have a one-to-many relationship, that is, a process contains multiple threads.
The rendering process, of course, is multi-threaded, so let’s take a look at what threads the rendering process contains.
GUI rendering thread
- Responsible for rendering pages, layout and drawing
- This thread executes when the page needs to be redrawn and reflow
- Mutually exclusive with JS engine threads to prevent unexpected render results
JS engine thread
- Responsible for parsing and executing javascript scripts
- Only one JS engine thread (single thread)
- Mutually exclusive with GUI rendering threads to prevent unpredictable render results
Event-triggered thread
- Used to control event loops (mouse click, setTimeout, Ajax, etc.)
- When the event meets the triggering condition, the event is put into the execution queue where the JS engine is located
Timed trigger thread
- The thread on which setInterval and setTimeout reside
- Timed tasks are not timed by the JS engine, but by the timed triggering thread
- When the timing is over, the notification event fires the thread
Asynchronous HTTP request threads
- The browser has a separate thread that handles AJAX requests
- When the request completes, notifies the event firing thread if there is a callback function
When we look at these threads in the rendering process, we think about two things:
- Why is javascript single-threaded
- Why is the GUI rendering thread mutually exclusive with the JS engine thread
Why is javascript single-threaded
The first is historical. When javascript was created, multi-process and multi-threaded architectures were not popular and hardware support was not good.
Second, because of the complexity of multithreading, multithreaded operations need to be locked, coding complexity will increase.
Also, if you manipulate the DOM at the same time, you can end up rendering the DOM unpredictably when multiple threads are not locked.
Why is the GUI rendering thread mutually exclusive with the JS engine thread
This is because JS can manipulate the DOM, and if you modify the element attributes and render the interface at the same time (that is, the JS thread and UI thread are running simultaneously), then you might not get the same elements before and after the rendering thread.
Therefore, in order to prevent unexpected results from rendering, the browser sets the GUI rendering thread and JS engine thread to be mutually exclusive. When the JS engine thread executes, the GUI rendering thread will be suspended, and GUI updates will be saved in a queue to be executed immediately when the JS engine thread is idle.
View JS operation mechanism from Event Loop
So this is where we finally get to our topic, what is an Event Loop
Understand some concepts:
- JS is divided into synchronous tasks and asynchronous tasks
- Synchronization tasks are executed on the JS engine thread, forming a
Execution stack
- Event-triggered threads manage one
Task queue
, the asynchronous task triggering condition is reached, and the callback event is placed inTask queue
In the Execution stack
At this time, the JS engine thread is idle, and the system will readTask queue
To add a runnable asynchronous task callback event toExecution stack
In, the execution starts
In front end development, we specify scheduled tasks with setTimeout/setInterval. We send network requests via XHR/ FETCH. What do setTimeout/setInterval and XHR/ FETCH do
We know that both setTimeout/setInterval and XHR/ FETCH codes are synchronous tasks while the callbacks are asynchronous tasks.
When the code is executed at setTimeout/setInterval, it is actually the JS engine thread that tells the trigger thread that a callback event will be triggered after an interval of one time. The trigger thread, after receiving the message, will wait for a certain amount of time. Put the callback event into an event queue managed by the event firing thread.
When code execution to the XHR/fetch, is actually the JS engine threads to inform asynchronous HTTP requests, thread sends a network request, and make requests after the completion of the callback event, and asynchronous HTTP requests threads after received the news, will be after the request is successful, the callback event into a thread managed by event is triggered by the event queue.
When our synchronization task is finished, the JS engine thread will ask the event triggering thread whether there is any callback function to be executed in the event queue. If there is, it will be added to the execution stack and handed over to the JS engine thread for execution
To illustrate:
Let’s explain it in code:
let timerCallback = function() {
console.log('wait one second');
};
let httpCallback = function() {
console.log('get server data success');
}
// Synchronize tasks
console.log('hello');
// Synchronize tasks
// After notifying the timer thread for 1s, the timerCallback will be handled by the event-triggered thread
// After 1s, the event-triggering thread adds the timerCallback to the event queue
setTimeout(timerCallback,1000);
// Synchronize tasks
// Notifies the asynchronous HTTP request thread to send the network request, and sends the httpCallback to the event-triggered thread when the request succeeds
// If the request succeeds, the event-triggering thread will add httpCallback to the event queue
$.get('www.xxxx.com',httpCallback);
// Synchronize tasks
console.log('world');
/ /...
// After all synchronization tasks are completed
// Ask the event triggering thread if there is any callback function in the event event queue that needs to be executed
// If not, keep asking until you do
// If so, add the callback event to the execution stack and start executing the callback code
Copy the code
To summarize:
- The JS engine thread only executes events in the execution stack
- When the code in the execution stack finishes executing, the events in the event queue are read
- The callback events in the event queue are inserted into the event queue by the respective threads
- The cycle
Macro task, micro task
When we have a basic understanding of what is an execution stack and what is an event queue, let’s take a closer look at macro tasks and micro tasks in the event loop
What is a macro task
We can think of the code executed on each execution stack as a macro task (including getting an event callback from the event queue and putting it on the execution stack each time). Each macro task is executed from the beginning to the end, and nothing else is executed.
As mentioned earlier, the JS engine thread and THE GUI rendering thread are mutually exclusive. In order to make macro and DOM tasks proceed in order, the GUI rendering thread will start to render the page after the result of one macro task is executed and before the next macro task is executed.
// Macro task --> Render --> Macro task --> Render --> Render...
Copy the code
The main code block, setTimeout, setInterval, etc., is a macro task
First example:
document.body.style = 'background:black';
document.body.style = 'background:red';
document.body.style = 'background:blue';
document.body.style = 'background:grey';
Copy the code
We can put this code in the browser console and execute it as follows:
As a result, the background of the page will instantly turn gray. The above code is part of the same macro task, so the rendering of the page will not be triggered until it is complete. During rendering, the GUI thread will optimize and merge all the UI changes, so visually, the page will only turn gray.
Second example:
document.body.style = 'background:blue';
setTimeout(function(){
document.body.style = 'background:black'
},0)
Copy the code
Let’s do that and see what happens:
I will see that the page first appears as a blue background, and then suddenly as a black background. This is because the above code belongs to two macro tasks. The first macro task executes the code to change the background to blue, then triggers the render to change the page to blue, and then triggers the second macro task to change the background to black.
What is a microtask
We already know that when the macro task finishes, rendering is performed and then the next macro task is executed, while a microtask can be understood as a task that is executed immediately after the current macro task executes.
That is, when the macro task finishes executing, all the microtasks generated during execution are executed before rendering.
Promise, process, nextTick, etc., are microtasks.
First example:
document.body.style = 'background:blue'
console.log(1);
Promise.resolve().then(() = >{
console.log(2);
document.body.style = 'background:black'
});
console.log(3);
Copy the code
Let’s do that and see what happens:
The console prints 1, 3, and 2 because the callback to the PROMISE object’s THEN method executes asynchronously, so 2 is printed last
The background color of the page goes straight to black, without going through the blue phase, because we set the background to blue in the macro task, but performed the microtask before rendering, which changed the background to black before rendering
Second example:
setTimeout(() = > {
console.log(1)
Promise.resolve(3).then(data= > console.log(data))
}, 0)
setTimeout(() = > {
console.log(2)},0)
// print : 1 3 2
Copy the code
The above code contains two settimeouts, which means there are two macro tasks besides the main code block. The first macro task outputs 1 and creates a microtask queue. Therefore, the microtask is executed before the next macro task queue is executed. The next macro task is executed, and 2 is displayed
conclusion
- Perform a
Macro task
If there is no one in the stackThe event queue
Get) - During the execution, if the
Micro tasks
, it is added toMicro tasks
In the task queue Macro task
The current command is executed immediately after the execution is completeMicrotask queue
All of theMicro tasks
(Execute in sequence)- The current
Macro task
So that’s done, start checking the render, and thenThe GUI thread
To take over the rendering - After rendering,
JS thread
Keep taking over and move on to the next oneMacro task
(Obtained from the event queue)
Article Series
- How does “Front end Advanced” gracefully handle image exceptions
- “Front-end advanced” single-page route resolution and implementation
- “Front-end progression” thoroughly understand function currying
- “Front-end advanced” JS stack memory heap memory
- Memory management in “front-end advanced” JS
- The front-end advanced array is out of order
reference
- Inside WebKit technology
- Which processes are included in the browser
- A simple explanation of processes and threads