preface
Recently, I was in a daze before preparing the output sequence of console for the interview questions. Note: the information is from the network, I just sort it out.
You’re supposed to know
JS is single threaded
Single threading is one of the core features of JavaScript. This means that in JS all tasks need to be queued until the first task is finished. This creates a problem: if the first task takes a long time, the second task has to wait for the first task to finish. For example, we request a piece of data to the server. Due to network problems, it may take about 60 seconds to successfully return the data. At this time, we can only process the following code after waiting for the completion of the request.
Synchronous and asynchronous tasks
To solve the single threaded JS problem, JavaScript divides all tasks into synchronous and asynchronous tasks.
Synchronous task
A synchronization task is a task that can be executed immediately after the current (if any) task has been executed. These tasks are queued up for execution on the main thread. Which means standing in line
//for(){} and console.log() will be executed in sequence, finally printing 0, 1, 2, 3, 4 done.
for (let i = 0; i < 5; i++) {
console.log(i)
}
console.log('done')
Copy the code
Asynchronous task
Compared with synchronous tasks, asynchronous tasks do not need to queue into the main thread for execution, but enter the overtaking lane and merging lane. That’s a series of tasks in a task queue. These tasks are re-executed on the main thread only when they are notified that they can be executed.
// The following then() method, which waits for the Promise to be resolved (), is an asynchronous task. The final output is 1, 3, 2.
console.log(1)
Promise.resolve().then(() = > {
console.log(2)})console.log(3)
Copy the code
In particular, all synchronization tasks are queued up for Execution on the main thread, forming an Execution Context Stack. In addition to the main thread, there is a task queue. When an asynchronous task has a result, an event is placed in the task queue. When all synchronous tasks in the execution stack are completed, the asynchronous tasks in the task queue enter the execution stack and continue their execution.
Asynchronous tasks (task queues) can be divided into
-
Macrotasks (taskQueue) : macrotasks, also known as task queues
- Macrotasks :(note the order!)
- (1) setTimeout (delayed call)
- (2) setInterval
- (3) setImmediate Mediate
- (4) requestAnimationFrame (high frequency RAF)
- (5) I/O (I/O operation)
- (6) UI rendering
- (7) The JS code wrapped in a script tag is also a macrotask
- Macrotasks :(note the order!)
Note: (1) Each MacroTask callback should be executed at the beginning of the next batch! (2) Only setImmediate ensures that the next event loop is handled immediately
-
Microtasks: Tasks that are scheduled to be executed immediately after the execution of the current script to avoid the cost of an additional task.
- Microtasks :(note the order!)
- (1) Process. nextTick (Define an action in Node and let the action be executed at the next event polling point)
- (2) Promises (details see the article: www.jianshu.com/p/06d16ce41…
- (3) Object.observe (native observer implementation, deprecated)
- (4) MutationObserver (listening for DOM change) processes other microtasks only when nextTick is empty. (Next tick queue has even higher priority over the Other Micro tasks queue.)
- Microtasks :(note the order!)
The order in which an eventLoop is executed (very important) :
- ① Start executing the script.
- (2) Select the first task in macroTasks (taskQueue) and execute the callback function at the beginning of the next task.
- (3) Execute all microtasks in the microtasks one by one. After the microtasks are completed, add microtasks and continue to execute them until the microtask queue is empty.
- (4) Select the second task in macroTasks (taskQueue) and execute the callback function at the beginning of the next task.
- ⑤ Take all microtasks from the microtasks and execute them one by one. After the microtasks are completed, you can add microtasks and continue to execute them until the Microtask queue is empty.
- ⑥ Loop ② ③ until macrotasks and microtasks are empty.
The reason a Promise can’t catch an error in a setTimeout callback with a catch is because Promise’s THEN /catch is executed before setTimeout.
The order of the event loop determines the order in which the JavaScript code is executed. It starts the first loop with script (the whole code). The global context then enters the function call stack. Until the call stack is empty (only global), then all microtasks are executed. After all executable MicroTasks have been executed. The loop starts again with MacroTasks, finds one of the task queues to complete, and then executes all microtasks, and so on.
Execute all Microtasks in the Microtasks queue, and select a Macrotasks queue to execute all Macrotasks in it. Then continue to execute all Microtasks in the Microtasks queue and select a Macrotasks queue to execute all Macrotasks in it…
This explains why Microtasks are executed before Macrotasks in the same event loop.
exercises
The exercise is based on the following figure. Note that the macro task will be executed next time. This time, the micro task needs to be executed first, and then refer to the following figure
Problem sets 1
console.log(1)
setTimeout(() = >{
console.log(2)},0)
process.nextTick(() = >{
console.log(3)})new Promise((resolve) = >{
console.log(4)
resolve()
}).then(() = >{
console.log(5)})Copy the code
Problem number 1
The first event loop
Main process | Macro event queue | Micro event queue |
---|---|---|
console.log(1) | setTimeout | process |
console.log(4) | then |
- The synchronization task is performed first, printing 1 in the order in which it appears
- When a setTimeout is encountered, put in the Macro Event Queue
- Put the Micro Event Queue when a Process is encountered
- When a promise is encountered, execute immediately, printing 4, and place the then callback in the Micro Event Queue
- Then look at the Micro Event Queue, execute it one by one, print 3, print 5
- The first Event Loop completes
Second cycle of events
Main process | Macro event queue | Micro event queue |
---|---|---|
setTimeout | ||
- Take the Macro Event Queue and put it first into the main process
- The output of 2
- Micro Event Queue has no tasks
- The second round of Event Loop is complete
Problem set 2
console.log(1)
setTimeout(() = > {
console.log(2)},0)
process.nextTick(() = > {
console.log(3)})new Promise((resolve) = > {
console.log(4)
resolve()
}).then(() = > {
console.log(5)})setTimeout(() = > {
console.log(6)},0)
new Promise((resolve) = > {
console.log(7)
setTimeout(() = > {
console.log(8)
resolve()
}, 0)
}).then(() = > {
console.log(9)
setTimeout(() = > {
console.log(10)
new Promise((resolve) = > {
console.log(11)
resolve()
}).then(() = > {
console.log(12)})},0)})// 1, 4, 7, 3, 5, 2, 6, 8, 9, 10, 11, 12
Copy the code
Problem 2
The first event loop
Main process | Macro event queue | Micro event queue |
---|---|---|
console.log(1) | setTimeout2 | process3 |
console.log(4) | setTimeout6 | then5 |
console.log(7) | setTimeout8 |
- Output of main process: 1, 4, 7
- Execute the first Micro Event Queue: print 3
- The second Micro Event Queue: prints 5
- The Micro Event Queue is cleared. The first round is complete
Second cycle of events
Main process | Macro event queue | Micro event queue |
---|---|---|
setTimeout2 | setTimeout6 | |
setTimeout8 |
- Main flow output 2
- If the Micro Event Queue is empty, the second round is complete
The third cycle of events
Main process | Macro event queue | Micro event queue |
---|---|---|
setTimeout6 | setTimeout8 | |
- Main flow output 6
- The third round is completed
The fourth cycle of events
Main process | Macro event queue | Micro event queue |
---|---|---|
setTimeout9 | setTimeout10 | then9 |
- Note that the then callback is pressed to the Micro Event Queue after output 8
- The then9 callback is executed, printing 9
- A new setTimeout is added to the Macro Event Queue
- There is nothing to execute in this loop, end
The fifth cycle of events
Main process | Macro event queue | Micro event queue |
---|---|---|
console.log(10) | then12 | |
console.log(11) |
- In round 5, setTimeout10 enters the main flow and outputs 10
- If you encounter a promise, print 11
- Resolve, press then into the Micro Event Queue
- Execute Micro Event Queue with output 12
Problem set 3
// The following code runs in the Node environment: process.nextTick is provided by Node
console.log("1")
setTimeout(function () {
console.log("2")
process.nextTick(function () {
console.log("3")})new Promise(function (resolve) {
console.log("4")
resolve()
}).then(function () {
console.log("5")
})
})
process.nextTick(function () {
console.log("6")})new Promise(function (resolve) {
console.log("Seven")
resolve()
}).then(function () {
console.log("8")})setTimeout(function () {
console.log("9")
process.nextTick(function () {
console.log("10")})new Promise(function (resolve) {
console.log("11")
resolve()
}).then(function () {
console.log("12")})})// Final output: 1 7 6 8 2 4 3 5 9 11 10 12
Copy the code
Problem sets 4
setTimeout(() = >{
console.log("setTimeout1");
Promise.resolve().then(data= > {
console.log(222);
});
},0);
setTimeout(() = >{
console.log("setTimeout2");
},0);
Promise.resolve().then(data= >{
console.log(111);
});
//111 setTimeout1 222 setTimeout2
Copy the code
Problem number 4
- There is no code to execute on the main thread
- You then encounter setTimeout 0, which puts the callback function into the macro task queue after 0ms (the task will be executed in the next event loop).
- We then encounter setTimeout 0, which will put the callback function into the macro task queue after 0ms (the task will be executed in the next event loop).
- First check the microtask queue, that is, the microTask queue, find that it is not empty, execute the first Promise’s then callback, printing ‘111’.
- If the microTask queue is empty, enter the next event loop, check the macro task queue, find the setTimeout callback function, immediately execute the callback function ‘setTimeout1’, check the MicroTask queue, find the queue is not empty, execute the promise then callback, Output ‘222’, the MicroTask queue is empty, and the next event loop is entered.
- Check the macro task queue and find the setTimeout callback function, immediately execute the callback function to print ‘setTimeout2’.
Problem set 5
console.log('script start');
setTimeout(function () {
console.log('setTimeout---0');
}, 0);
setTimeout(function () {
console.log('setTimeout---200');
setTimeout(function () {
console.log('inner-setTimeout---0');
});
Promise.resolve().then(function () {
console.log('promise5');
});
}, 200);
Promise.resolve().then(function () {
console.log('promise1');
}).then(function () {
console.log('promise2');
});
Promise.resolve().then(function () {
console.log('promise3');
});
console.log('script end');
/*
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0
*/
Copy the code
Problem 5
- The synchronization tasks on the main process are performed in sequence, the first and last lines of console.log
- You then encounter setTimeout 0, which puts the callback function into the macro task queue after 0ms (the task will be executed in the next event loop).
- Then you encounter setTimeout 200, which will put the callback function into the macro task queue after 200ms (the task will be executed in the next event loop).
- Perform the then callback of the first promise, printing ‘promise1’, and then execute the then callback of the second promise, printing ‘promise3’. Since the return of the first promise.then () is still a promise, the second.then() is put into the microTask queue to continue execution, printing ‘promise2’;
- If the microTask queue is empty, enter the next event loop and check the macro task queue. If the callback function of setTimeout is found, execute the callback function immediately and output ‘setTimeout– 0’. Then check the MicroTask queue and enter the next event loop if the queue is empty.
- Check the macro task queue and find the setTimeout callback function, execute the callback function immediately and print ‘setTimeout– 200’.
- SetTimeout 0 is used to place the callback function on the macro task queue after 0ms, check the microtask queue, find that the queue is not empty, perform the promise’s then callback, and print ‘promise5’.
- If the microTask queue is empty, enter the next event loop, check the macro task queue, find the setTimeout callback function, execute the callback function output immediately, print ‘inner-setTimeout– 0’. Code execution is complete.
Problem sets 6
console.log("1");
setTimeout(function cb1(){
console.log("2")},0);
new Promise(function(resolve, reject) {
console.log("3")
resolve();
}).then(function cb2(){
console.log("4");
})
console.log("5")
// 1, 3, 5, 4, 2
Copy the code
Problem number 6
Problem sets 7
console.log("1");
setTimeout(() = > {
console.log("2")
new Promise(resolve= > {
resolve()
}).then(() = > {
console.log("3")})},0);
setTimeout(() = > {
console.log("4")},0);
console.log("5")
// 1 5 2 3 4
Copy the code
Problem number 7
Problem sets 8
console.log("1");
setTimeout(() = > {
console.log("2")
new Promise(resolve= > {
console.log(6)
resolve()
}).then(() = > {
console.log("3")})},0);
setTimeout(() = > {
console.log("4")},0);
console.log("5")
// 1 5 2 6 3 4
Copy the code
Problem 8
Problem sets 9
console.log('start')
setTimeout(function(){
console.log('Macro Task 1')})Promise.resolve().then(function(){
console.log('Micromission zero')})console.log('Execution stack in execution')
setTimeout(function(){
console.log('Macro Task 2')
Promise.resolve().then(function(){
console.log('Micromission one')})},500)
setTimeout(function(){
console.log('Macro Task 3')
setTimeout(function(){
console.log('Macro Task 4')
Promise.resolve().then(function(){
console.log('Micromission 2')})},500)
Promise.resolve().then(function(){
console.log('Micromission 3')})},600)
console.log('end')
// start Execution stack Execution End Micro task 0 Macro task 1 Macro task 2 Micro task 1 macro task 3 Micro task 3 Macro task 4 Micro task 2
Copy the code
Problem number 9
Problem sets 10
function test() {
console.log(1)
setTimeout(function () { // timer1
console.log(2)},1000)
}
test();
setTimeout(function () { // timer2
console.log(3)})new Promise(function (resolve) {
console.log(4)
setTimeout(function () { // timer3
console.log(5)},100)
resolve()
}).then(function () {
setTimeout(function () { // timer4
console.log(6)},0)
console.log(7)})console.log(8)
//1
Copy the code
Problem number 10
Combine our above JS operation mechanism to look at this problem is much simpler and clearer
- JS is executed sequentially from top to bottom
- Execute to test(), the test method is synchronized, execute directly, console.log(1) prints 1
- In the test method, setTimeout is an asynchronous macro task, and the callback is called timer1 and put into the macro task queue
- Then execute the test method with a setTimeout for the asynchronous macro task, call it timer2 and put it in the macro task queue
- And then we execute the promise, new promise is the synchronization task, execute it directly, print 4
- The setTimeout in new Promise is an asynchronous macro task, and the callback is called timer3 and put into the macro task queue
- Promise.then is the microtask, put on the microtask queue
- Console. log(8) is a synchronization task that is executed directly and prints 8
- When the main thread task completes, check for promise. then in the microtask queue
- SetTimeout is an asynchronous macro task. Call it timer4 and put it in the macro task queue
- Console. log(7) in the microtask queue is a synchronization task, executed directly, printing 7
- The microtask completes, and the first loop ends
- Check the macro task Queue, which contains timer1, Timer2, timer3, timer4, four timer macro tasks, according to the timer delay time can be executed in order, namely, Event Queue: Timer2, Timer4, Timer3, and Timer1 are placed at the end of the execution stack.
- Run timer2, console.log(3) for synchronization and print 3
- Check that there are no microtasks. The second Event Loop ends
- Run timer4, console.log(6) for synchronization and print 6
- Check that there are no microtasks. The third Event Loop ends
- Perform timer3, console.log(5) synchronization and print 5
- Check that there are no microtasks. The fourth Event Loop ends
- Perform timer1, console.log(2) synchronization and print 2
- 1,4,8,7,3,6,5,2
Problem sets 11
setTimeout(() = > {
console.log(1)},0)
new Promise((resolve, reject) = > {
console.log(2)
resolve(3)
}).then(val= > {
console.log(val)
})
console.log(4)
// 2, 4, 3, 1
Copy the code
Problem 11
Problem sets 12
for (let i = 0; i < 5; i++) {
console.log(i)
}
console.log('done')
// 0 1 2 3 4 done
Copy the code
Problem number 12
Problem sets 13
console.log(1)
Promise.resolve().then(() = > {
console.log(2)})console.log(3)
/ / 1 2 3
Copy the code
Problem number 13
Problem sets 14
setTimeout(() = > {
console.log(1)},0)
for (let i = 2; i <= 3; i++) {
console.log(i)
}
console.log(4)
setTimeout(() = > {
console.log(5)},0)
for (let i = 6; i <= 7; i++) {
console.log(i)
}
console.log(8)
//2 3 4 6 7 8 5
Copy the code
Problem number 14
Problem sets 15
console.log(1)
async function async1() {
await async2()
console.log(2)}async function async2() {
console.log(3)
}
async1()
setTimeout(() = > {
console.log(4)},0)
new Promise(resolve= > {
console.log(5)
resolve()
})
.then(() = > {
console.log(6)
})
.then(() = > {
console.log(7)})console.log(8)
// 1 3 5 8 2 6 7 4
Copy the code
Problem number 15
Problem sets 16
console.log(1)
function a() {
return new Promise(resolve= > {
console.log(2)
setTimeout(() = > {
console.log(3)},0)
resolve()
})
}
a().then(() = > {
console.log(4)})/ / 1 2 3 4
Copy the code
Problem number 16
Problem sets 17
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
//script start, script end, promise1, promise2, setTimeout
Copy the code
Problem number 17
-
The whole script enters the main thread as the first macro task, encounters console.log, and prints script start
-
When a setTimeout is encountered, its callback function is dispatched to the macro task Event Queue
-
When a Promise is encountered, its then function is assigned to the micro-task Event Queue, named THEN1, and then it is encountered and assigned to the micro-task Event Queue, named then2
-
When console.log is encountered, print script end. So far, there are three tasks in the Event Queue, as shown in the following table:
| main process | Macro event queue | Micro event queue | | — – | — — — — — – | — — — — — – | | the console. The log (‘ script start ‘) | console.log(‘setTimeout’) | console.log(‘promise1’) | | console.log(‘script end’) | | console.log(‘promise1’) |
Problem sets 18
console.log('script start');
setTimeout(function() {
console.log('timeout1');
}, 10);
new Promise(resolve= > {
console.log('promise1');
resolve();
setTimeout(() = > console.log('timeout2'), 10);
}).then(function() {
console.log('then1')})console.log('script end');
//script start, promise1, script end, then1, timeout1, timeout2
Copy the code
Problem 18
First, the event loop starts from the macroTask queue. Initially, there is only one script task in the macroTask queue. When a task source is encountered, the task is first distributed to the corresponding task queue. So, similar to the above example, console.log is encountered and script start is printed; Next, it encounters the setTimeout task source and dispatts it to the task queue, denoting timeout1. Then it encounters a promise, the code in the new Promise executes immediately, prints promise1, executes resolve, encounters setTimeout, dispatches it to the task queue, Call it timemout2, then distribute it to the microtask queue, call it then1; Then the macro task queue is checked. If the macro task queue is empty, the macro task queue is checked. Then the macro task queue is checked. Execute timeout1 and print timeout1. Then execute timeout2, which prints timeout2. At this point, all queues are cleared, and execution is complete. The output sequence is: script start, promise1, script end, then1, timeout1, timeout2.
Here’s a tip: MicroTask execution takes precedence over Task execution by specification, so if there is logic that needs to be executed first, putting it into the MicroTask queue will be executed before the task. Finally, remember that JavaScript is a single-threaded language, and asynchronous operations are placed in an event loop queue waiting for the main execution stack. There is no dedicated asynchronous execution thread.
Problem sets 19
console.log(1)
setTimeout(function() {
console.log(2)},0)
setTimeout(function() {
console.log(3)},0)
console.log(4)
// 1, 4, 2, 3
Copy the code
Problem number 19
Problem sets 20
function fun1(){
console.log(1)}function fun2(){
console.log(2)
fun1()
console.log(3)
}
fun2()
/ / 1 2 3
Copy the code
Problem number 20
Problem sets 21
function func1(){
console.log(1)}function func2(){
setTimeout(() = >{
console.log(2)},0)
func1()
console.log(3)
}
func2()
/ / 1 2 3
Copy the code
Problem number 21
Problem sets 22
var p = new Promise(resolve= >{
console.log(4) // there is no execution of p but there is output, so 4 is the initial
resolve(5)})function func1(){
console.log(1)}function func2(){
setTimeout(() = >{
console.log(2)},0)
func1()
console.log(3)
p.then(resolve= >{
console.log(resolve)
})
}
func2()
//4, 1, 3, 5, 2
Copy the code
Problem 22
Problem sets 21
console.log('start')
const interval = setInterval(() = > {
console.log('setInterval')},0)
setTimeout(() = > {
console.log('setTimeout 1')
Promise.resolve()
.then(() = > {
console.log('promise 1')
})
.then(() = > {
console.log('promise 2')
})
.then(() = > {
setTimeout(() = > {
console.log('setTimeout 2')
Promise.resolve()
.then(() = > {
console.log('promise 3')
})
.then(() = > {
console.log('promise 4')
})
.then(() = > {
clearInterval(interval)
})
}, 0)})console.log('time end')},0)
Promise.resolve().then(() = > {
console.log('promise 5')
}).then(() = > {
console.log('promise 6')})// start
// promise 5
// promise 6
// setInterval
// setTimeout 1
// time end
// promise 1
// promise 2
// setInterval
// setTimeout 2
// setInterval
// promise 3
// promise 4
Copy the code
Problem 22
(1) Divide code into macroTask and Microtask:
console.log('start')
Copy the code
SetInterval is macroTask whose callback is executed after MicroTask
const interval = setInterval(() = > {
console.log('setInterval')},0)
Copy the code
SetTimeout is a MacroTask whose callback is executed in cycle 2
setTimeout(() = >. .0)
Copy the code
The two THEN’s () of promise.resolve () are microtasks
Promise.resolve()
//microtask
.then(() = > {
console.log('promise 5')})//microtask
.then(() = > {
console.log('promise 6')})Copy the code
(2) First bus (Cycle 1) : loading
The first macroTask is setInterval, which executes at the beginning of cycle 2. The second macroTask is setTimeout, which executes at the beginning of cycle 3.
Empty the stack, output: start Executes microtasks until the queue is empty, the two THEN () of promise.resolve (), output: Promise 5 Promise 6
(3) Second bus (Cycle 2) : Execute setInterval callback, output: SetInterval, and the next setInterval is also a MacroTask but is placed after cycle 4, the next cycle 3 setTimeout
There are no microtasks in setInterval, so the queue is empty, so proceed to the next cycle (cycle 3).
(4) The third bus (cycle 3) executes the setTimeout callback and outputs setTimeout 1 to execute microtasks until the queue is empty, i.e. the first and second then () of promise.resolve (),
Output: Promise 1 Promise 2
The setTimeout in the third THEN () is macroTask, which is placed in cycle 5 to execute the callback, and the fourth THEN () is immediately followed by the third THEN (), so it is executed in cycle 5
At this point, the microtasks are empty, so proceed to the next cart (cycle 4)
(5) The fourth bus (cycle 4) is obtained from (3), and setInterval is executed. SetInterval is output
There are no microtasks in setInterval, so the queue is empty, so proceed to the next cycle (cycle 5).
The next setInterval is also a MacroTask, but cycle 6 is used to perform the callback,
(6) The fifth bus (‘cycle 5’) is obtained from (4) and executes setTimeout
Execute microtasks until the queue is empty, namely the first and second THEN () of promise.resolve (),
Output: Promise 3 Promise 4
Then execute the third then () –> clearInterval(interval) to clear the setInterval for the next cycle 6 callback
The MicroTasks are empty and the entire code completes.