One, the introduction
As we all know, JS is a single-threaded, asynchronous, non-blocking programming language. So how can a single thread be asynchronous?
Single-threaded vs. asynchronous
In JS, only one thread processes JS tasks, which means that all tasks can only be executed synchronously. If a network request is encountered, it must wait for the result of the request to return before JS can proceed. This is obviously implausible. So the Event Loop mechanism was introduced to help browsers deal with long-pending tasks.
Browser event loop model
This article does not involve the actual operating environment of the browser, but abstracts the event loop model of the browser.
Conceptual understanding
- The call stack
(Call stack)
: the unique worker thread of the JS engine, used for function call execution WebApis
Browser-provided events such as DOM operations, AJAX requests, timers, etc- Task queue
(task queue)
The: event loop queues the completed webApi events sequentially, and when the call stack is empty, the callback function at the top of the queue is pushed onto the call stack for execution - Microtask queue
(microtask queue)
: Each microtask in the microtask queue is executed in turn each time a task exits and the call stack is empty. The difference is that it waits until the microtask queue is empty before stopping execution — even if a microtask joins in mid-stream. In other words, a microtask can add new microtasks to the queue and complete all of the microtasks before the next task starts and the current event loop ends.
Event loop flow (not including rendering)
- The call stack is empty after all tasks on the call stack are executed.
- perform
microtask queue
All of themicrotask
“, even if they join in the middlemicrotask
; Such asmicrotask queue
If no, go to 3. - perform
Task Queue
The company firsttask
; If the call stack is empty, go to 2.
For example
<script>
function task1() {
console.log('task1')
task2()
return
}
function task2() {
console.log('task2')
task3()
return
}
function task3() {
console.log('task3')
return
}
document.body.addEventListener('click'.() = > {
setTimeout(function handleClick(){
console.log('handleClick called')})})new Promise((resolve) = > {
document.body.addEventListener('click'.() = > {
resolve('promise')
})
}).then(function success(value) {
console.log(value)
})
task1()
document.body.click()
</script>
Copy the code
- The main function
main
Push the call stack, code is executed from top to bottom, function declarationtask1,task2,task3
; - add
body
theclick
Event handlerhandleClick
Go to WebAPIs and wait for the event to fire. - add
promise
whenbody
theclick
Event triggered when calledresolve('promise')
; task1
The call,task1
Push call stack execution, console output'task1'
;task1
Internal callstask2
.task2
Push call stack execution, console output'task2'
;task2
Internal callstask3
.task3
Push call stack execution, console output'task3'
;task3
After execution, return and pop up call stack;task2
After execution, return and pop up call stack;task1
After execution, return and pop up call stack;document.body.click()
.click
Event firing, listening to function execution;handleClick
pushtask queue
Waiting for execution;resolve
The call,success
pushmaricotask queue
Waiting for execution;main
After execution, return and pop up call stack; Call stack empty;- The event loop mechanism checks that the call stack is empty and executes
microtask queue
The callback function insuccess
, console printing'promise'
; - check
microtask queue
If no, executetask queue
The callback function inhandleClick
, console printing'handleClick called'
;