Hello, everyone, I am Yiyi Lin, the following article knowledge event Loop is important in JS, this article will take you to understand how the event Loop works and include classic interview questions, start reading 😆.
Mind mapping
Basic concepts of JS asynchronous programming
JS is single-threaded because browsers (multithreaded) only allocate one thread to execute the JS code. They only allocate one thread because browsers are concerned about the problems caused by multithreading. Suppose JS is multi-threaded and one thread adds content to a DOM node. Another thread deletes content on this node, so which one should the browser execute? So JS is designed to be single threaded. But a single thread can cause a lot of tasks to wait to execute, so the browser’s event loop mechanism was introduced.
Process and thread tips
A process can include multiple threads. For example, when a page is opened, the page occupies one process of the computer. When the page is loaded, browsing will allocate one thread to calculate the DOM tree, one thread to execute JS code, and other threads to load resource files.
Second, the event loop
The main thread of JS continuously reads and executes tasks from the task queue in a loop. This operation mechanism is called event loop. It is recommended to see the event Loop in 2 minutes
Macro and micro tasks
The browser event loop is divided into macro tasks and micro tasks. JS tasks are divided into synchronous tasks and asynchronous tasks.
1. Macro Task
Most of the tasks performed by the main stack in JS, such as: Timers, event bindings, callback functions, and the FS module in Node are the macro tasks script, setTimeout, setInterval, setImmediate(Node), I/O(Node), AND UI rendering
2. Micro Tasks
Promise, async/await, process.nextTick, MutationObserver, etc are microtasks.
Consider: Why microtasks, only macro tasks?
Microtasks are introduced to solve the problem of asynchronous callback. Assuming that there are only macro tasks, then the callback function is also put into the macro task queue after each macro task is executed. This will cause how long the queue will be, and the callback time will become longer, which will cause the page to lag, so the introduction of microtasks.
Think, why does the code after await enterpromise
Microtasks in queues?
Async /await is just syntactic candy for manipulating promises, and ultimately promises. Take a little chestnut
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
// The above code is equivalent to ==>
async function async1() {
console.log('async1 start');
Promise.resolve(async2()).then(() = > {
console.log('async1 end')})}Copy the code
3. Order of execution of macro and micro tasks (important)
- The main stack queue is a macro task. After each macro task is completed, the micro tasks in the macro task will be executed. Until all the micro tasks are completed, the next macro task will be executed.
- The order of execution priority of tasks in JS is: main stack global task (macro task) > micro task in macro task > next macro task., so
Promise (Microtasks)
The execution sequence has a higher prioritysetTimeout
The timer.- Can not see the general
.then
Is put into the microtask queue; Because there’s no callResolve or reject
Previously, asynchronous tasks were not completed, so callbacks cannot be randomly placed into the microtask event queueawait
Is a sign to give up threads.await
The following expression will be executed first, and willawait
The following code is added tomicro task
The microtask ispromise
Microtasks in the queue, and then the whole thing pops outasync
Function to continue with the following code.process.nextTick
Is an independent ofeventLoop
The macro tasks in the main stack are executed first after each completionprocess.nextTick
Queue, and perform the microtaskpromise
的.then()
.- The page UI is rendered after each macro task and microtask of the macro task is executed.
Warm up 1 look at a chestnut first
/ / A task
setTimeout(() = > {
console.log(1)},20)
/ / B task
setTimeout(() = > {
console.log(2)},0)
/ / C task
setTimeout(() = > {
console.log(3)},10)
// D
setTimeout(() = > {
console.log(5)},10)
console.log(4)
/* Output * 4 -> 2-> 3 -> 5 -> 1 */
Copy the code
The main task (macro task) on the main thread is executed from top to bottom, and setTimeout code encountered is next. So will be added to the waiting queue, the browser has special listen to code in the waiting queue, synchronization code execution is completed in the main stack, waiting in the queue to the execution time of the first first task execution, if wait for task queue has two to the execution time of asynchronous code at the same time, the first in the queue is first performed to the main stack. Therefore, wait for task B in the queue to execute, then task C, task D, and task A. The output is 4 -> 2-> 3 -> 1.
Warm up 2 Change the chestnuts on top
/ / A task
setTimeout(() = > {
console.log(1)},20)
/ / B task
setTimeout(() = > {
console.log(2)},0)
/ / C task
setTimeout(() = > {
console.log(3)},30)
console.log(4)
/* Output * 4 -> 2-> 1 -> 3 */
Copy the code
Task A is executed before task C, so it outputs 1 and then 3.
var Func = () = > console.log('a');
setTimeout(Func,0);
console.log('change');
Func = () = > console.log('another a');
// output change, a
Copy the code
SetTimeout (Func, 0) is not another a because the address of setTimeout(Func, 0) is already in the macro task queue. If the address of setTimeout(Func, 0) is changed, it does not affect the callback function Func. So the output is going to be a.
Event loop in node
After node11, the execution sequence of nodes is the same as that of browsers. Before node11, the execution sequence of nodes is different from that of browsers
Three key stages
- The Timers phase executes the callback functions of setTimeout and setInterval and is controlled by the poll phase.
- Poll is an important stage
- Check whether there is a timer. If there is a timer and the execution time has reached, it will be executed and the eventLoop will return to the Timers phase
- No timer, will look at the poll event loop queue. If the poll queue is not empty, the swap queue is traversed and executed until the queue is empty.
- If the poll queue is empty. So I’m going to look to see if the setImmediate function needs to be executed. So if there’s a setImmediate mediate that’s going to be done then the poll phase stops and then the Check phase does the setImmediate. Without the setImmediate() poll phase waits a while for the callback to be added to the channel queue and executed immediately.
- When the waiting time limit expires, the system automatically enters the check phase.
Before Node 10:
- Complete all missions in a phase
- Finish executing the nextTick queue
- Then execute the contents of the microtask queue
Three, think of the output sequence (Chrome browser shall prevail)
1. Take a thought problem and find the output
let xhr = new XMLHttpRequest()
xhr.open('post'.'api')
xhr.onreadystatechange = () = >{
if(xhr.readyState == 2) {console.log(2)}if(xhr.readyState == 4) {console.log(4)
}
}
xhr.send()
console.log(3)
/* Output * 3, 2, 4 */
Copy the code
2. Another thought: what does the following code output in a synchronous request
let xhr = new XMLHttpRequest()
xhr.open('get'.'xxx'.false)
xhr.send()
xhr.onreadystatechange = () = > {
console.log(xhr.readyState)
}
Copy the code
No output, the above two questions in the interview | Ajax, fetch, axios uhf interview question parsing.
3. An Ajax asynchronous thinking problem to find the output result.
let xhr = new XMLHttpRequest()
xhr.open('post'.'api')
xhr.onreadystatechange = () = >{
console.log(xhr.readyState)
}
xhr.send()
/* Output * 2 -> 3 -> 4. * /
Copy the code
Xhr.onreadystatechange is asynchronous and is added to the wait queue. The ajax status code changes to 1 after the main task executes xhr.send(). Xhr. onreadyStatechange in the idle waiting task starts to listen for the status code change and does not change until the status code changes from 2 -> 3 -> 4. If you are not familiar with ajax status code can look at the interview | ajax, fetch, axios uhf interview questions.
4. Make a promise
console.log(1)
new Promise((resolve, reject) = > {
console.log(2)
resolve()
}).then(res= > {
console.log(3)})console.log(4)
/* Output * 1 -> 2 -> 4 ->3 */
Copy the code
Answer: The first macro task is the synchronization task in the main stack, which outputs 1, JS code executes until Promise executes 2 immediately, resolve executes.then() code into the microtask queue, outputs 4 when the macro task ends, and finally executes the microtask queue output 3
5. Execution sequence of setTimeout and Promise
setTimeout(function () {
console.log(1)},0);
new Promise(function (resolve, reject) {
console.log(2);
resolve();
}).then(function () {
console.log(3)
}).then(function () {
console.log(4)});console.log(6);
// 2, 6, 3, 4, 1
Copy the code
Answer: Start the macro task in the main stack first, throw it into the macro task queue when setTimeout is encountered, execute output 2 immediately when promise is encountered, and resolve() asynchronously throw it into the micro task queue. Finally, output 6, the remaining micro task after the execution of the first macro task ends, i.e..then() output 3, 4. The first loop ends and the next macro task setTimeout begins, printing 1.
6. Execution sequence of setTimeout and Promise
setTimeout(function () {
console.log(1)},0);
new Promise(function (resolve, reject) {
console.log(2)
for (var i = 0; i < 10000; i++) {
if (i === 10) {
console.log(10)
}
i == 9999 && resolve();
}
console.log(3)
}).then(function () {
console.log(4)})console.log(5);
// 2, 10, 3, 5, 4, 1
Copy the code
So this is going to be the same problem as above, where you have to distinguish between macro tasks and micro tasks.
7. Find the output
console.log("start");
setTimeout(() = > {
console.log("children2")
Promise.resolve().then(() = >{
console.log("children3")})},0)
new Promise(function(resolve, reject){
console.log("children4")
setTimeout(function(){
console.log("children5")
resolve("children6")},0)
}).then(res= >{ // flag
console.log("children7")
setTimeout(() = >{
console.log(res)
}, 0)})// start children4 children2 children3 children5 children7 children6
Copy the code
- Start with the first round of macro tasks in the main task, output
start
Encountered,setTimeout
It does not need to wait for 0s but directly puts the macro task into the queueconsole.time/timeEnd
To test), encounteredpromise
Execute output nowchildren4
“And met anothersetTimeout
The first round of macro tasks is completed and no microtasks are performed. Q: Above.then()
(commentflag
Is the microtask of the first macro task cycle? No! becauseresolve
There was no implementation,promise
The state has not yet been removedpending
Change, it’s not the first round of microtasks.- Start the next round of macro tasks and execute the first one entered
setTimeout
And the outputchildren2
The second round of macro tasks ends and the micro tasks startpromise
In the.then()
The outputchildren3
. The second cycle ends- And then it started again
setTimeout
Macro task, outputchildren5
, microtask outputchildren7
. Here we encounter a macro tasksetTimeout
To the macro task queue.- And start a new
setTimeout
Macro task, output RESchildren6
.
8. (Headline) Please write down the running results of the following code (the output varies under different environments, and the latest Chromium is subject below)
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')}async function async2() {
console.log('async2')}console.log('script start')
setTimeout(function () {
console.log('setTimeout')},0)
async1()
new Promise((resolve) = > {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')})console.log('script end')
/ / output
//script start
//async1 start
//async2
//promise1
//script end
//async1 end
//promise2
//setTimeout
Copy the code
The difficulty in this problem is whether to output promise2 or async1 end first. Console. log(‘async1 end’) after await async2() goes into the microtask queue in the Promise when executing from above the global macro task, Finally, console.log(‘promise2’) from.then() enters the promise microtask queue. So it prints async1 end and then promise2 before it starts the next macro task loop. Start the next macro task setTimeout and output setTimeout.
9. Change the previous topic and calculate the output (the output varies under different environments, the latest Chromium is subject below)
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise3');
resolve();
}).then(function () {
console.log('promise4');
});
console.log('script end');
//script start,
// async1 start,
// promise1,
// promise3,
// script end,
/ / promise2,
/ / async1 end,
// promise4,
// setTimeout
Copy the code
Start global macro tasks with script start, Async1 start, promise1, promise3, script end. Which await async2 (); , async2(). Then () enters the promise microtask queue and await async2(); The next code enters the promise’s task queue, console.log(‘promise4’); Finally, it enters the promise’s task queue. The macro task ends and the microtask starts. Promise2, AsynC1 end, and promise4 are output in the promise microtask queue according to the first-in, first-out rule. The global microtask ends, the next macro task setTimeout starts, and setTimeout is output.
10. Let’s change the above topic again and calculate the output (the output varies under different environments, the latest Chromium is subject below).
async function async1() {
console.log('async1 start');
await async2();
setTimeout(function() {
console.log('setTimeout1') // This part of the code is put into the Promise microtask queue.
},0)}async function async2() {
setTimeout(function() {
console.log('setTimeout2')},0)}console.log('script start');
setTimeout(function() {
console.log('setTimeout3');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
// script start, async1 start, promise1, script end, promise2, setTimeout3, setTimeout2, setTimeout1
Copy the code
SetTimeout3: setTimeout3: setTimeout3: setTimeout3: setTimeout3: setTimeout3: setTimeout3: setTimeout3 Then add setTimeout2 and setTimeout1 to the macro task queue. So the output is setTimeout3, setTimeout2, setTimeout1
- Microtask queue
new Promise((resolve, reject) = > {
console.log('B');
resolve(); / / 1
}).then(() = > { // First then
console.log('C')
new Promise((resolve, reject) = > {
resolve() / / 2
}).then(() = > {
console.log('D')
}).then(() = > { / / 3
console.log('E')
})
}).then(() = > { // The second then joins
console.log('F')});Copy the code
1 the resolve (); Resolve () is a microtask that is queued first. Resolve () is empty and C is output first. Resolve () at 2 is also a microtask placed in the microtask queue. The first THEN is queued, and the second THEN is queued (D). The third THEN is queued asynchronously. The second THEN is queued (F), and the last THEN is queued (E)
Four, reference
Javascript Ninja Secrets, 2nd edition, Event Loop
Interview question: Tell me about the cycle of events.
Question 10: common asynchronous pen questions, please write the running results of the code
Do you really use Promise
Five, the end
Js asynchronous queue topic will first stop here, if you feel not satisfied can see this articleThe interview | | JS, you have to understand the asynchronous programming promiseIf this post is helpful or inspiring, please leave it on GithubstarThis is Lin Yiyi. See you next time