It is well known that JavaScript is run on a single thread and can be executed asynchronously. In general, such single-threaded and asynchronous languages are event-driven, and browsers provide such an environment for JavaScript
setTimeout(function(argument) {
console.log('- 1 - - -)
}, 0)
console.time("test")
for (var i = 0; i < 1000000; i+ +) {
i = = = (100000 - 1)
}
console.timeEnd("test")
console.log('- 2 -)
Copy the code
The output on my computer is:
Test: 5.4892578125 ms
2 — — — — — —
1 — — — — — —
Oh, it doesn’t make sense ah, clearly I set 0 milliseconds after the print ‘–1–‘ situation, open the front bible look, there is a sentence:
This is because even though setTimeout was called with a delay of zero, it’s placed on a queue and scheduled to run at the next opportunity; not immediately. Currently-executing code must complete before functions on the queue are executed, thus the resulting execution order may not be as expected.
After the waiting time of setTimeout ends, it is not executed directly, but pushed into a task queue of the browser first. After the synchronization queue ends, tasks in the task queue are called successively.
This involves several threads in the browser, and typically browsers have the following threads
-
Js engine threads (interpreting and executing JS code, user input, network requests)
-
GUI thread (draws the user interface and is mutually exclusive with the JS main thread)
-
HTTP network request thread (processing users’ GET, POST and other requests, etc., and pushing the callback function to the task queue after the result is returned)
-
Timing trigger thread (setTimeout, setInterval after waiting time to push the execution function into the task queue)
-
Browser event processing thread (putting click, Mouse, and other interactive events into an event queue after they occur)
Execution stacks and callbacks for JavaScript functions
function test1(a) {
test2(a)
console.log('Hi, I'm test1')
}
function test2(a) {
console.log('Hi, I'm Test2')
}
function main(a) {
console.log('Hello, I'm Main')
setTimeout(() = > {
console.log('Hi, I'm setTimeout.')
}, 0)
test1(a)
}
main(a)
Copy the code
The result is as follows:
Hello, I’m Main
Hello, I’m Test2
Hello, I’m Test1
Hello, I’m setTimeout
When we call a function, its address, arguments, and local variables are all pushed into a stack
Step1: main() is called first, goes to the stack and prints’ hello, I’m main ‘
Step2: when setTimeout is encountered, put the callback function into the task queue
Step3: Main calls test1. Test1 enters the stack and is executed
Step4: Execute test1, and test1 invokes test2
Step5: test2 Execute, print ‘Hello, I am test2’
Step6: after test2 is executed, pop back to test1 from the stack and print ‘hello, I’m test1’.
Step6: after the execution of the main thread, enter the callback queue and execute the callback function of setTimeout. Print “hello everyone, I am setTimeout”. By now, the whole program is finished, but the event loop is waiting for other callback functions.
It looks something like this in code
while (queue.waitForMessage()) {
queue.processNextMessage(a)
}
Copy the code
The thread is always waiting for other callbacks such as click, setTimeout, etc
Use a graph to represent it as follows:
Macrotask and microtask
So let’s look at a little bit of code and think about how this works out, right
setTimeout1 = setTimeout(function(a) {
console.log('- 1 - - -)
}, 0)
setTimeout2 = setTimeout(function(a) {
Promise.resolve(a)
.then(() = > {
console.log('- 2 -)
})
console.log('- 3 -)
}, 0)
new Promise(function(resolve) {
console.time("Promise")
for (var i = 0; i < 1000000; i+ +) {
i = = = (1000000 - 1) && resolve(a)
}
console.timeEnd("Promise")
}).then(function(a) {
console.log('- 4 -)
});
console.log('- 5 -)
Copy the code
According to the above analysis, the browser will put the asynchronous callback function into a task queue. According to this analysis, when the program runs, the setTimeout function will be encountered first, and the setTimeout callback function will be put into the task queue. Further down, the setTimeout function will be encountered again. The setTimeout callback has a Promise object in it, but let’s wait until it’s in the task queue to execute, and then the browser interpreter will encounter an operation on the New Promise object, which is not executed asynchronously, A program run timer will start, and the time it takes to increment from 0 to 1000000 will be printed, and at I = 999999, the Promise state will change to resolve and the callback that resolve executes will be pushed into the task queue, and at the end of the program, The output is’ –5– ‘.
According to the above analysis, our program output order is:
The time required for the program to increment from 0 to 1000000
5 — — — — — —
1 — — — — — —
– 3 –
— — — — – 4
2 — — — — — —
Use a graph to represent it as follows:
Run it in the browser to see the result:
Promise: 5.151123046875 ms
5 — — — — — —
— — — — – 4
1 — — — — — —
– 3 –
2 — — — — — —
why
Because browsers have more than one task queue, there are also microtasks and MacroTasks
microtasks:
-
process.nextTick
-
promise
-
Object.observe
-
MutationObserver
macrotasks:
-
setTimeout
-
setInterval
-
setImmediate
-
I/O
-
The UI rendering
According to the WHATWG specification:
-
An event loop can have one or more task queues.
-
Each event loop has a MicroTask Queue
-
task queue == macrotask queue ! = microtask queue
-
A task can be placed in a MacroTask Queue or a MicroTask Queue
-
The call stack is cleared (global only) and all microtasks are executed. When all executable microtasks have been executed. The loop starts again with macroTask, finds one task queue to complete, and then executes all microTasks, and so on
So, the correct execution steps should be:
Step1: execute the script and press the script to task queue
stacks: []
task queue: [script]
microtask queue: []
Copy the code
Step2: When setTimeout1 is encountered, setTimeout can be regarded as a task
stacks: [script]
task queue: [setTimeout1]
microtask queue: []
Copy the code
Step3: Proceed further and press setTimeout2 into the Task queue when it encounters setTimeout2
stacks: [script]
task queue: [setTimeout1.setTimeout2]
microtask queue: []
Copy the code
Step4: Continue the execution, new Promise, go down according to the synchronization process, output the execution time in new Promise
stacks: [script]
task queue: [setTimeout1.setTimeout2]
microtask queue: []
Copy the code
Step5: Resolve () occurs when I = 99999, and the successful callback is added to the MicroTask queue
stacks: [script]
task queue: [setTimeout1.setTimeout2]
microtask queue: [console.log('- 4 -)]
Copy the code
Log (‘– 5– ‘), and the task in the stacks queue is finished
stacks: []
task queue: [setTimeout1.setTimeout2]
microtask queue: [console.log('- 4 -)]
Copy the code
If stacks are empty, the event polling thread will push the task in the microTask queue to the stacks and then print”
stacks: []
task queue: [setTimeout1.setTimeout2]
microtask queue: []
Copy the code
If the microTask queue has an empty stack and the microtask queue has an empty stack, the task will be executed. If the microtask queue has an empty stack, if the microtask queue has an empty stack, the task will be executed. If the microtask queue has an empty stack, the task will be executed.
stacks: [setTimeout1.setTimeout2]
task queue: []
microtask queue: []
Copy the code
Step8: setTimeout2 then encounters a promise.resolve (), presses the successful callback to the microTask queue, and prints’ –3– ‘.
stacks: [setTimeout2]
task queue: []
microtask queue: [Promise.resolve()]
Copy the code
Step9: Execute microTask queue after setTimeout2 is finished, print ‘–2–‘, and the code section is finished
stacks: []
task queue: []
microtask queue: []
Copy the code
In a simple sentence: the entire JS code MacroTask executes first, the synchronous code executes after the microTask executes the microTask, no MicroTask executes the next MacroTask, and so on to the end of the loop
— — — — — — — — —
Long press the QR code to follow the big Zhuan FE