JS Study Notes - In-depth understanding of macro tasks, micro tasks, and event loops in browsers and Node.js
JavaScript is single-threaded
JavaScript is a single-threaded scripting language, that is, JS code can only run in one process. In other words, JS can only execute one task at the same time. But if all the codes are executed synchronously, this will cause serious problems. For example, if we want to get some data from the remote end, should we keep looping the code to judge whether the returned result has been obtained?
So there is the concept of asynchronous events, registering a callback function, such as sending a network request, we tell the main program to notify me after receiving the data, and then we can do other things.
Then after the asynchronous completion, we will be notified, but the program may be doing other things at this time, so even if the asynchronous completion needs to wait, wait until the program is free to have time to see which asynchronous has been completed, you can go to implement.
Introduction to Browser Threads
GUI rendering thread
Responsible for rendering browser interface HTML elements
JS engine thread
Responsible for processing the JavaScript script main program
timer trigger thread
Timer setInterval
and setTimeout
its thread
browser event thread
Used to control events, such as triggered onload, click events, etc.
http request thread
After XMLHttpRequest
connecting, a new thread request is opened through the browser. When a state change is detected, if a callback function is set, the asynchronous thread will generate a state change event and put it in the task queue of the JavaScript engine for processing .
Macrotasks and Microtasks
What are macro tasks and micro tasks?
macro task
setTimeout
setInterval
setImmediate
(Node.js)requestAnimationFrame
(browser)UI Rendering
(browser)I/O
script
micro task
Promise.then() Promise.catch() Promise.finally()
MutationObserver
Object.observe
async await
process.nextTick
(Node)
The browser's event loop
Synchronous and asynchronous tasks enter different execution "places", synchronously enter the main thread, and asynchronously enter the Event Table and register functions. When the specified thing is completed, Event Table will move this function into the task queue (Event Queue). When the task in the main thread is empty after execution, go to the task queue (Event Queue) to read the corresponding function and enter the main thread for execution.
The browser's macro task and micro task mechanism
JS asynchrony also has a mechanism, that is, when encountering a macro task, execute the macro task first, put the macro task into the task queue (event queue), and then execute the micro task, put the micro task into the micro task queue (micro task queue), However, these two queues are not a queue
order of execution
Main thread >> microtasks created on main thread >> macrotasks created on main thread >> main thread >> microtasks created on main thread >> macrotasks created on main thread...
example
console.log(1)
setTimeout(()=>{
console.log(2)
},0)
new Promise((resolve,reject)=>{
console.log(3)
resolve()
}).then(res=>{
console.log(4)
})
console.log(5)
setTimeout(()=>{
new Promise((resolve,reject)=>{
console.log(6)
}).then(res=>{
console.log(7)
})
},0)
console.log(8)
/*
控制台输出:
1
3
5
8
4
2
6
/// 7不输出因为该Promise状态仍为pending,且无法变为resolved
*/
The event loop mechanism of Node.js
Description of each stage of Event Loop
stage | describe |
---|---|
timers | Callback function for processing setTimeout andsetInterval |
pending callbacks | Callbacks to perform certain system operations |
idle,prepare | Used internally by Node, ignored |
poll | Wait for new I/O events to trigger, and execute I/O-related callbacks |
check | When the execution of the poll phase is completed, it will enter the execution of the check phase, and the execution content of this phase is all setImmediate callbacks |
close callbacks | When the socket is closed abnormally, close the event callback will be executed at this stage |
Macrotask and microtask queues in Node
macro task queue
- timers queue
- pending callbacks queue
- check queue
- close queue
microtask queue
- next Tick Queue
- other Micro Queue
poll polling phase
Poll is a core stage, waiting for the triggering of new I/O events, and executing I/O-related callbacks
The main function:
- 执行到期的
setTimeout
和setInterval
的回调函数 - 处理poll队列中的事件
工作机制:当没有timers被调度,分两种情况:
- 如果poll队列不为空,会挨个执行队列里的callback,直到队列为空,或达到系统限制
- 如果poll队列为空,分两种情况:
- 如果执行了
setImmediate
,eventLoop
会结束poll阶段,进入到check阶段执行 - 如果没有执行
setImmediate
,eventLoop
会等待callback进入队列
- 如果执行了
一旦poll队列为空,
evetloop
会检查timers,如果计时已到,event loop 会回到 timers 阶段,执行相应的回调函数.
执行顺序
timers
- nextTickQueue
- otherMicroQueue
pending callbacks
- nextTickQueue
- otherMicroQueue
idle,prepare
- nextTickQueue
- otherMicroQueue
poll
(可能返回到timers阶段)- nextTickQueue
- otherMicroQueue
check
- nextTickQueue
- otherMicroQueue
close callbacks
示例
console.log(0);
setTimeout(() => {
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
setImmediate(() => {
console.log(3);
})
process.nextTick(() => {
console.log(4);
})
}, 0);
setImmediate(() => {
console.log(5);
process.nextTick(() => {
console.log(6);
})
})
setTimeout(() => {
console.log(7);
process.nextTick(() => {
console.log(8);
})
}, 0);
process.nextTick(() => {
console.log(9);
})
console.log(10);
/* Node.js 控制台输出:
0
10
9
1
4
7
8
5
6
3
2
*/