Let’s take a look at browser processes and threads first

  • First, browsers are multi-process, such as:
    • Browser main process
    • Third-party plug-in processes
    • GPU process
    • Process TAB page
  • There can be one or more threads in a process

Browser process

  • When a TAB page is opened in the browser, the corresponding TAB page process is started. Each page is not affected
  • A page is a process, and a process has multiple threads working together

The process in the browser TAB page consists mainly of the following threads

Main thread (JS engine thread)

  1. Browsers have many threads (event-triggered threads interact with each other)

GUI rendering thread (resident thread)

  1. Render browser interface, parse HTML, CSS, build DOM tree, layout and draw
  2. This thread is mutually exclusive with the JS thread. When the JS engine executes, the engine will be suspended and stored in a queue until the JS engine is idle

JS engine thread (resident thread) (JS kernel)

  1. Responsible for handling JavaScript scripts (e.g. V8 engine), parsing JavaScript scripts, running code
  2. Only one JS thread is running the JS program on a page
  3. If the JS engine runs for too long, it will block page loading and affect the GUI rendering thread page loading
  4. When the JS engine is modifying the DOM, if the GUI engine is running at the same time, the page data will be inconsistent
  5. So GUI updates are placed in a task queue and executed as soon as the JS engine is idle

Browser event thread (onclick) (resident thread)

  1. This event belongs to the browser and is used to control the event loop
  2. The JS engine will add these tasks to the event thread when it encounters events (events that are not synchronized first), DomEvent events, etc
  3. When the event meets the trigger condition is triggered (such as click, move and other operations), the thread will add the event to the end of the task queue, waiting for the JS engine to process
  4. Since JS is a single thread, the task queue needs to wait for the JS engine to process it in turn
  5. Timer triggers thread (setTimeout and setInterval)
  6. Timers are not counted by the JS engine because JS is single-threaded and blocking affects timing accuracy
  7. Therefore, when the timing is complete, it is added to the end of the task queue, waiting for the JS engine to execute
  8. Attention!!
    1. If the JS engine is processing other tasks, the timer time is exceeded
    2. For example, if the timer is 5000ms and the loop event is 6000ms, even if the timer thread puts the execution function into the end of the task event queue at 5000ms, it can only be called after the loop ends, which causes that the timer is triggered when the actual 6000ms is 6000ms

Asynchronous HTTP request threads

  1. When the XMLHttpRequest connects, the browser opens a new thread to request it
  2. OnreadystateChange = function () {} when a state change is detected
  3. If there is a callback function, the asynchronous thread generates a state change event, puts the callback function at the end of the event queue, and executes it by the JS engine

Event Loop

Event loop Event polling

  • First, how does the JS engine work

The stack

  1. Last in, first out, first in, last out
  2. Data stores can only be stored and taken out one by one from the top

The heap

  1. Unordered key-value storage

The queue

  1. First in first out
  2. Data stores are inserted from the back of the queue and taken from the head

Event to perform

  1. The JS engine first processes the synchronous code (macro task), and the asynchronous code such as micro task and timer is put into the corresponding thread to wait for triggering
  2. When microtasks and asynchronous code meet the trigger criteria, the callback function is placed at the end of the task queue waiting for the JS engine to execute
  3. When an event event is triggered by a user operation, the event triggering thread will place the corresponding event function at the end of the task queue

Task priority

Graph LR id1 ==> ID2 == synchronous task --> ID3 == asynchronous task --> ID4 style id1 fill:# CCF,stroke-width:4px; style id2 fill:#ccf,stroke-width:4px; style id3 fill:#ccf,stroke-width:4px; style id4 fill:#ccf,stroke-width:4px;

Task execution line

Graph TB starts executing queue tasks... --> Macro task == micro task ==> GUI rendering engine thread ==...... --> Macro task 2 == micro task ==> GUI rendering engine thread 2 -- Event Loop --> Start executing queue task...

Macro (synchronous) and micro (asynchronous) tasks

Graph TB A[" execute queue task "] A== Enter queue task -->B[sync or asynchronous task] B== sync -->C(execute immediately) == Complete queue task --> Continue queue task; B== async -->D[timer trigger thread] == Register the corresponding callback function --> HTTP trigger thread == Async function is placed at the end of the queue --> continue to execute the queue task == Complete --> ID > loop to execute the queue task] --> A

let oneTask = () = > {
    console.log('First macro task');
    queueMicrotask(() = > console.log('First microtask'));
}
oneTask()
setTimeout(() = > console.log('Timer task in the middle'));
Promise.resolve('promise task').then((result) = > console.log(result));
let twoTask = () = > {
    console.log('Second macro task');
    queueMicrotask(() = > console.log('Second microtask'));
}
twoTask()
Copy the code

Graph LR Macro tasks --> Micro Tasks --> Asynchronous timer tasks

Again, macro tasks (synchronization tasks) tend to be placed in event queues first

  1. Ordinary functions: Belong to macro tasks
  2. QueueMicrotask: Creates a microtask
  3. Promise: Microtasks, too
  4. SetTimeout: indicates an asynchronous task

Asynchronous tasks

  • Here are two more asynchronous tasks

window.requestAnimationFrame

  1. This Api acts as a request animation frame
  2. Animation can be implemented using Css3, Canvas, this Api is dedicated to request animation
    • Screen refresh rate: The number of images appearing on the screen per second. Common notebook: 60Hz
    • Principle of animation: the computer takes 16.7ms per 1Hz, which looks like smooth movement due to the visual pause of human eyes
    • SetTimeout: Indicates an asynchronous scheduled task
      1. Tasks in the queue are executed only when the main thread task is finished, so the actual execution time is always later than the set time
      2. The fixed interval of asynchronous scheduled tasks may not be the same as the screen refresh time, causing frame loss
    • RequestAnimationFrame: Callback tasks are performed only based on the frequency of computer refreshes and the Render interface is timed
let progress = 0;
// The callback function
console.time()
const render = () = > {
 progress += 1; // Change the position of the image
 if (progress < 100) {
 // Render recursively before the animation ends
     window.requestAnimationFrame(render);
 } else {
     console.timeEnd()
 }
}
// Render first frame
window.requestAnimationFrame(render)
Copy the code

window.requestIdleCallback

  1. This Api queues functions that are called during browser downtime
  2. Below the window. The priority requestAnimationFrame
  3. Call requestIdleCallback() in the idle callback function to schedule another callback before passing through the event loop the next time.
window.requestIdleCallback(() = > console.log('Idle call this function'));
Copy the code
Graph LR Macro Task --> Micro Task --> requestAnimationFrame --> requestIdleCallback --> Asynchronous timed task

conclusion

  1. A TAB browser page can be thought of as having a macro task queue and a micro task queue
  2. When the script is loaded, synchronous code (macro task) is executed first, and when the execution stack is empty, it is taken out from the microtask queue and put into the execution stack for execution
  3. After the execution stack completes the micro-task, wait for the next macro task or micro-task to enter the execution stack and continue to execute the task, thus forming an event cycle.