preface
As we all know, Javascript is a single-threaded, non-blocking scripting language. This means that Javascript has only one main thread to perform all tasks, and various tasks must be queued to execute. So why is it that browsers can do long tasks and still respond to user interface interactions? What about non-blocking? Let’s explore it in detail.
The renderer process and main thread
First of all, what is the relationship between the browser process and the Javascript main thread? A TAB page in a browser is a render process. A render process consists of multiple threads, including the following four threads:
- Main thread
- Worker thread Worker thread
- Compositor threads
- Raster thread
The Javascript code runs in the main thread
Macro and micro tasks
Asynchronous tasks can be divided into macrotasks and microtasks. Macro tasks include:
- setTimeout
- setInterval
- setImmediate
- I/O operations
- The UI rendering
- MessageChannel
- postMessage
Microtasks include:
- Promise
- Object.observe
- MutationObserver
- process.nextTick(Node.js)
- queueMicrotask
The Javascript engine divides tasks into two queues based on these two types.
Event loop mechanism
In the process of executing the code, the Javascript engine will execute the synchronous task immediately. When it encounters the asynchronous task, it will suspend the asynchronous task temporarily and hand it to the browser kernel for execution. When all synchronization tasks are completed. Because the browser will keep to check whether there is a task in the queue waiting for task execution, if there is a small task, will perform all tasks, the task queue to empty, again into the task in the first task in the main thread, completes the task will be to check again if there is a small task, until all the tasks in the task queue is completed. This cycle continues until all asynchronous tasks have been executed. When the browser kernel completes an asynchronous task, it puts the asynchronous task callback function into the task queue and waits for the next event loop to execute all the callback functions in the task queue.
The instance
After understanding the mechanism of the event loop, it becomes so easy to answer the following thought questions 🙂! Consider the printout after executing the following code:
setTimeout((a)= >{
console.log(1)
Promise.resolve().then((a)= >{
console.log(5)
setTimeout((a)= >{
console.log(7)},0)})},0)
setTimeout((a)= >{
console.log(2)},0)
Promise.resolve().then((a)= >{
console.log(3)
setTimeout((a)= >{
console.log(4)},0)})console.log(6)
Copy the code
The correct answer is 6,3,1,5,2,4,7
Why is that? Let’s make a specific analysis:
- The synchronization task in the main thread is executed first, so the first output is 6
- The microtask executes first with the macro task, so the second output is 3
- The first setTimeout should be executed after the completion of the microtask, so the output is 1
- After the first setTimeout completes, the existing microtask is executed, so 5 is printed
- The second setTimeout is executed after the second microtask completes, so the output is 2
- When the second microtask completes, the third setTimeout in the task queue is executed, so 4 is output
- The last setTimeout in the task queue is then executed, printing 7
conclusion
After reading this article, do you feel that Javascript event loops are very simple? 😏 Finally, we summarize what we have learned this time:
- Javascript runs in the main thread, and tasks need to be queued
- Task queues are first in, first out
- Microtasks are executed first with macro tasks
- After the microtask is complete, only one macro task is executed and then the microtask is checked again
- The browser suspends pending asynchronous tasks, waits for them to complete, and queues the callback function for the next event loop to execute temporarily