First, js single thread problems
Js is single-threaded and processes tasks one after another, so if one task takes a long time to process, subsequent tasks will block
Therefore, JS solves this problem through the Event Loop. Before understanding the Event Loop, we need to know some keywords
What are stack, Queue, heap, event loop
- Stack: Throw up if you eat too much
- Queue: eating too much… The release of
- Heap: Stores OBJ objects
Execution stack
When the JS engine runs, when the code starts to run, it pushes the code onto the execution stack for execution
Ex. :
When the code is parsed, the functions are pushed on the stack in turn
If you push it, you push it out, and when c is done, you start pushing it out
When the execution stack encounters asynchrony
It’s in and out, but it’s synchronous, and synchronous means it blocks, so it needs to be asynchronous, so what happens when there’s asynchronous code in the stack
At this point in the code, add the click event and setTimeout, and now observe the execution order
Observe the stack effect at this point, and the above function nesting is significantly different
1, console.log(“sync”) statements are not pushed to the bottom of the stack because the console is finished
2, Click and setTimeout are both pushed, but their internal console is not pushed, indicating that they are not finished executing
3. If click is not completed, why is setTimeout pushed and should not be blocked?
The answer is: when the browser finds an asynchronous task in the execution stack, it hands it off to WebAPI for maintenance, and the execution stack continues to perform subsequent tasks
Also, setTimeout will be added to the WebAPI
Webapi is a browser implementation that maintains events.
Above, there is a progress bar next to setTimeout. This progress is the set waiting time
Callback queue
In the example above, when the setTimeout execution ends, should we go back to the execution stack and execute the output?
Answer: Not really!
At this point, the setTimeout executable after the countdown ends is put into the callback queue
Finally, the setTimeout executable is removed from the callback queue and placed on the execution stack again
This execution is called an event loop
The detailed process of Event Loop
A task is fetched from the head of the callback queue after the stack task is cleared
Here is the simplest example, with the output 1,3,2
Why is that?
The figure above shows the order of execution:
1, console.log (1) is pushed onto the execution stack
SetTimeout is recognized as an asynchronous task in the execution stack and put into webapis
Console.log (3) is pushed onto the execution stack while the setTimeout executable waits in the callback queue
4, console.log(3) After execution, fetch console.log(2) from the head of the callback queue and put it on the execution stack
5, console.log(2) execute
Callback queue first in first out
Note that the callback queue is first-in, first-out, for example:
When console.log(4) completes, console.log(2) is retrieved from the callback queue;
Note: Console.log (3) will not be fetched from the callback queue until the execution of console.log(2) is complete and the stack is emptied again.
Test if the concept is correct
The code above finally prints 1,5,2,4,3, and executes:
1, prints 1, and pushes 2 into the callback queue
2. Push 4 into the callback queue
3, output 5
4, clears the stack, reads output 2, finds 3, and pushes 3into the callback queue
5, clears the stack of execution and reads output 4
6, clears the stack of execution and reads output 3
At this point, it seems to be ok, but !!!!!! The matter is not over yet
Macrotask, Microtask
Now that you know a little bit about Event loop from the example above, let’s move on to another example
console.log(1);
setTimeout(()=>{
console.log(2)
})
var p = new Promise((resolve,reject)=>{
console.log(3)
resolve("Success")
})
p.then(()=>{
console.log(4)
})
console.log(5)
Copy the code
According to the concept of event loop, it would be 1,3,5,2,4, because setTimeout and then would be placed in the callback queue, and then first in, first out again, so it would be 2 and 4
But the order of facts is 1,3,5,4,2!
This is because promise’s then method is considered to be in a Microtask queue
What is a Macrotask?
A Macrotask is a callback queue
What are microtasks?
A Microtask is also a queue of tasks that are executed in order after the stack has been emptied
I’m going to graph it
You can see the Macrotask the callback queue and then there’s a Microtask on top of it
Microtasks are queued, but they are not placed one by one on the execution stack. When the execution stack is empty, all the tasks in the Microtask queue are executed, and the first Macrotask in the callback queue is fetched last.
Ex. :
The above execution is as follows:
1. Assign setTimeout to the macro task push
2. Push THEN (2) into the microtask
3. Push THEN (4) into the microtask
4. If the task queue is empty, take the first then(2) of the micro-task and push it into the execution stack
5, output 2, push then(3) into the microtask
6. If the task queue is empty, take the first then(4) of the micro-task and push it into the execution stack
7, output 4
8. If the task queue is empty, take the first then(3) of the micro-task and push it into the execution stack
9, output 3
The task queue is empty, so is the microtask.
11, output 1
Why is then a microtask
This depends on each browser, and each browser implements different promises. Some THEN are macro tasks, some are micro-tasks, and Chrome is micro-tasks, which generally default to micro-tasks
In addition to then, several other events are also recorded as microtasks:
- process.nextTick
- promises
- Object.observe
- MutationObserver
console.log("start");
setImmediate(()=>{
console.log(1)
})
Promise.resolve().then(()=>{
console.log(4);
})
Promise.resolve().then(()=>{
console.log(5);
})
process.nextTick(function foo() {
console.log(2);
});
process.nextTick(function foo() {
console.log(3);
});
console.log("end")
Copy the code
The code above prints start,end,2,3,4,5,1
Process. nextTick is not the same concept as then. It is added to the bottom of the execution stack, so it does not behave the same way
Final test
console.log("1");
setTimeout(()=>{
console.log(2)
Promise.resolve().then(()=>{
console.log(3);
process.nextTick(function foo() {
console.log(4);
});
})
})
Promise.resolve().then(()=>{
console.log(5);
setTimeout(()=>{
console.log(6)
})
Promise.resolve().then(()=>{
console.log(7);
})
})
process.nextTick(function foo() {
console.log(8);
process.nextTick(function foo() {
console.log(9);
});
});
console.log("10")
Copy the code
Order of execution:
1, output 1
2. Push setTimeout (2) into the macro task
3. Push THEN (5) into the microtask
4. Add nextTick to bottom of stack (8)
5, output 10
6. Execute nextTick (8)
7, output 8
Add nextTick (9) at the bottom of the stack
9, output 9
10, Perform microtasks then (5)
11, output 5
Push setTimeout (6) into the macro task
13. Push THEN (7) into the microtask
14, Perform microtasks then (7)
15, output 7
SetTimeout (2)
17, output 2
18. Push THEN (3) into the microtask
19, Perform microtasks then (3)
20, output 3
21. Add nextTick (4) at the bottom of the stack
22, output 4
SetTimeout (6)
24, output 6
The final results were: 1,10,8,9,5,7,2,3,4,6
reference
- Concurrency model and event loop
- Youtube video