directory

  1. How is ASYNCHRONOUS JS implemented
  2. The GUI thread
  3. JS engine thread
  4. Timer thread
  5. Event trigger thread
  6. Asynchronous HTTP request threads
  7. The browserSynchronization taskwithAsynchronous task (Macro and micro tasks)

How is ASYNCHRONOUS JS implemented

We all know that JS is single threaded, so how can single thread achieve asynchronous? In fact, “JS is single threaded “just means that there is only one main thread running JS, not the entire runtime environment is single threaded. JS running environment is mainly browser, to everyone is very familiar with the Chrome kernel as an example, he is not only multi-threaded, but also multi-process:

Chrome doesn’t have just one of each type of processes and threads. For example, there are multiple renderers. Each TAB has its own renderer. Sometimes when using Chrome, one of the tabs crashes or doesn’t respond, and the renderer for that TAB may crash, but other tabs don’t use that renderer, they have their own renderer, so the other tabs aren’t affected. That’s why Chrome doesn’t crash with a single page crash, as opposed to the old IE, where a page crash caused the entire browser to crash.

GUI threads

GUI threads are responsible for rendering pages, parsing HTML and CSS, and building them into DOM trees and rendering trees.

JS engine threads

This thread is responsible for the execution of the main JS thread, said previously “JS is single thread “refers to this thread. The famous Chrome V8 engine runs on this thread. Note that this thread is mutually exclusive with the GUI thread. The reason for the mutual exclusion is that JS can also manipulate the DOM, and if the JS thread and the GUI thread manipulate the DOM at the same time, the result will be confusing and it will not be known which result to render. The consequence of this is that if JS runs for a long time, the GUI thread will not execute and the entire page will feel stuck. So while(true) long synchronous code should never be allowed during actual development.

4. Timer thread

SetTimeout is actually run here, it is not in the same place as the main JS thread, so “single threaded JS” can be implemented asynchronously. JS timer methods and setInterval are also in this thread.

5. Event trigger thread

The timer thread is just a function of timing. It does not actually execute the callback when the time is up. It is the main JS thread that actually executes the callback. So when the time is up, the timer thread will pass the callback event to the event-triggering thread, which will then add it to the event queue. Eventually the JS main thread takes the callback from the event queue to execute. The event trigger thread not only puts timer events into the task queue, but also puts other events that meet the criteria into the task queue.

Asynchronous HTTP request threads

This thread is responsible for processing asynchronous Ajax requests, and when the request is complete, it notifies the event-triggering thread, which then puts the event into an event queue for the main thread to execute.

JS asynchronous implementation depends on the browser’s multithreading, when he meets the asynchronous API, the task will be given to the corresponding thread, when the asynchronous API meets the callback conditions, the corresponding thread through the event trigger thread will put the event into the task queue, and then the main thread from the task queue to continue to execute the event.

Seven, browserSynchronization taskwithAsynchronous task (Macro and micro tasks)

1) Event Loop of browser

An event loop is a loop that asynchronous threads use to communicate and execute cooperatively. Threads also have a common data area for exchanging messages, called the event queue. After each asynchronous thread completes its execution, the thread triggers the callback event to the event queue, and the main thread checks the queue for new work every time it finishes its work. If there is any new work, it takes it out and executes it. A flow chart looks like this:

The process is explained as follows:

  1. Each time the main thread executes, it looks at whether it is executing a synchronous task or an asynchronous API
  2. The synchronization task continues until it is complete
  3. When you encounter an asynchronous API, you hand it to the corresponding asynchronous thread and continue to perform the synchronization task yourself
  4. The asynchronous thread executes the asynchronous API and, when it’s done, places the asynchronous callback event on the event queue
  5. The main thread checks the event queue to see if there are any tasks when the synchronization task in hand is finished
  6. The main thread finds a task in the event queue and executes it
  7. The main thread repeats the above process

2) The timer is inaccurate

In the Event Loop process, there are some hidden holes. The most typical problem is that the synchronization task is always executed first, and then the callback in the Event queue is executed. This feature directly affects the execution of the timer. Let’s think about the process we started with the 2-second timer:

  1. The main thread executes synchronization code
  2. encountersetTimeoutAnd hand it to the timer thread
  3. The timer thread starts timing and the notification event triggers the thread in 2 seconds
  4. The event triggers the thread to put the timer callback into the event queue, and the asynchronous process ends there
  5. If the main thread is free, the timer callback is taken out and executed; if not, the callback is kept in the queue.

If the main thread is blocked for a long time, the timer callback will not be executed. If it is executed, the timer callback will not be executed properly.

const syncFunc = (startTime) = > {
  const time = new Date().getTime();
  while(true) {
    if(new Date().getTime() - time > 5000) {
      break; }}const offset = new Date().getTime() - startTime;
  console.log(`syncFunc run, time offset: ${offset}`);
}

const asyncFunc = (startTime) = > {
  setTimeout(() = > {
    const offset = new Date().getTime() - startTime;
    console.log(`asyncFunc run, time offset: ${offset}`);
  }, 2000);
}

const startTime = new Date().getTime();

asyncFunc(startTime);

syncFunc(startTime);

Copy the code

The result is as follows:

It can be seen from the result that although asyncFunc is called first and written to be executed after 2 seconds, syncFunc execution time is too long, reaching 5 seconds. Although asyncFunc has entered the event queue after 2 seconds, the main thread has been executing synchronous code and has not been available. The timer callback will not be executed until the synchronization code completes 5 seconds later. So again, don’t write code that’s tied up in the main thread for too long.

3) Introduce microtasks

In the previous flow chart, I simplified the event queue to make it easier to understand. In fact, there are two types of events in the event queue: macro tasks and micro tasks. Microtasks have a higher priority. When the event loop traverses the queue, the microtask queue is checked first, and if there are any tasks in it, they are all executed, and then a macro task is executed. Before executing each macro task, check whether there is any task in the microtask queue. If so, execute the microtask queue first. So the complete flow chart is as follows:

Here are a few things to note in the figure above:

  1. An Event Loop can have one or more Event queues, but only one microtask queue.
  2. The microtask queue is re-rendered once it has all been executed
  3. Each macro task is re-rendered after execution
  4. RequestAnimationFrame is in the render phase and is not in the microtask queue or macro task queue
1) Common macro tasks include:
  1. script(Can be understood as the outer synchronization code)
  2. setTimeout/setInterval
  3. setImmediate(Node.js)
  4. I/O
  5. UI events
  6. postMessage
2) Common microtasks include:
  1. Promise.then
  2. process.nextTick(Node.js)
  3. Object.observe(Abandoned)
  4. MutaionObserver
3) the demo
console.log('1');
setTimeout(() = > {
  console.log('2');
},0);
Promise.resolve().then(() = > {
  console.log('5');
})
new Promise((resolve) = > {
  console.log('3');
  resolve();
}).then(() = > {
  console.log('4');
})
// 1, 3, 5, 4, 2
Copy the code
Description:
  • Print 1 first, there’s nothing to say about that, synchronous code executes first, right

  • console.log(‘2’); In setTimeout, setTimeout is a macro task, and “2” enters the macro task queue

  • console.log(‘5’); In promise. then, enter the microtask queue

  • console.log(‘3’); In the argument to the Promise constructor, this is actually synchronous code, printed directly

  • console.log(‘4’); In then, it enters the microtask queue and performs the microtask first while checking the event queue

  • Synchronous code run result is “1,3”

  • Then check the microtask queue and print “5,4”

  • Finally, execute the macro task queue and print “2”

reference

  • Understand Event Loop thoroughly

conclusion

  • GUI threads are responsible for rendering pages, parsing HTML and CSS, and building them into DOM trees and rendering trees.

  • JS asynchronous implementation depends on the browser’s multithreading, when he meets the asynchronous API, the task will be given to the corresponding thread, when the asynchronous API meets the callback conditions, the corresponding thread through the event trigger thread will put the event into the task queue, and then the main thread from the task queue to continue to execute the event.

  • Event-firing threads are critical to implementing event loops.

  • When you write code, you should never use the main thread for long periods of time like let I =1000; while(i){ i–; . } will cause the timer to be more inaccurate

Conclusion 2

  • JS single thread only refers to the main thread only one, not the entire runtime environment is single thread

  • Asynchronous JS by the bottom of the multithreading implementation

  • Different asynchronous apis correspond to different implementation threads

  • Asynchronous threads communicate with the main thread through the Event Loop

  • When the asynchronous thread completes the task, it puts it into the task queue

  • The main thread constantly polls the task queue for tasks to be executed

  • Task queues are distinguished between macro task queues and micro task queues

  • The microtask queue has a higher priority and the macro task is not processed until all the microtasks have been processed

  • Promise is a microtask

  • The Node.js Event Loop is different from the browser Event Loop in that it is staged

  • SetImmediate and setTimeout(FN, 0) Mediate do not perform any of the preceding callbacks, depending on the phase in which they are registered. SetImmediate does not perform any of the following callbacks: If in the outermost layer or the setImmediate callback, which one executes first depends on the state of the machine at the time.

  • Process. nextTick is not in any phase of the Event Loop, it is a special API that executes immediately before continuing the Event Loop

The overall flow of the event loop

  1. Every time the main thread executes,Let's see if we want to execute a synchronous task or an asynchronous API
  2. Execute a synchronization task directly
  3. When you encounter an asynchronous API, call itTo the corresponding asynchronous threadAnd put it into the corresponding task queue,Continue to perform the outer script synchronization task yourself
  4. Script outer synchronization task, with the code in the Promise constructor, after execution
  5. priorityMicrotask queueMicrotasks in
  6. performMacro task queueMacro task in