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

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.)

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
  1. The synchronization task is performed first, printing 1 in the order in which it appears
  2. When a setTimeout is encountered, put in the Macro Event Queue
  3. Put the Micro Event Queue when a Process is encountered
  4. When a promise is encountered, execute immediately, printing 4, and place the then callback in the Micro Event Queue
  5. Then look at the Micro Event Queue, execute it one by one, print 3, print 5
  6. The first Event Loop completes

Second cycle of events

Main process Macro event queue Micro event queue
setTimeout
  1. Take the Macro Event Queue and put it first into the main process
  2. The output of 2
  3. Micro Event Queue has no tasks
  4. 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
  1. Output of main process: 1, 4, 7
  2. Execute the first Micro Event Queue: print 3
  3. The second Micro Event Queue: prints 5
  4. 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
  1. Main flow output 2
  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
  1. Main flow output 6
  2. The third round is completed

The fourth cycle of events

Main process Macro event queue Micro event queue
setTimeout9 setTimeout10 then9
  1. Note that the then callback is pressed to the Micro Event Queue after output 8
  2. The then9 callback is executed, printing 9
  3. A new setTimeout is added to the Macro Event Queue
  4. 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)
  1. In round 5, setTimeout10 enters the main flow and outputs 10
  2. If you encounter a promise, print 11
  3. Resolve, press then into the Micro Event Queue
  4. 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

  1. There is no code to execute on the main thread
  2. 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).
  3. 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).
  4. 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’.
  5. 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.
  6. 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

  1. The synchronization tasks on the main process are performed in sequence, the first and last lines of console.log
  2. 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).
  3. 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).
  4. 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’;
  5. 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.
  6. Check the macro task queue and find the setTimeout callback function, execute the callback function immediately and print ‘setTimeout– 200’.
  7. 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’.
  8. 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

  1. JS is executed sequentially from top to bottom
  2. Execute to test(), the test method is synchronized, execute directly, console.log(1) prints 1
  3. In the test method, setTimeout is an asynchronous macro task, and the callback is called timer1 and put into the macro task queue
  4. Then execute the test method with a setTimeout for the asynchronous macro task, call it timer2 and put it in the macro task queue
  5. And then we execute the promise, new promise is the synchronization task, execute it directly, print 4
  6. The setTimeout in new Promise is an asynchronous macro task, and the callback is called timer3 and put into the macro task queue
  7. Promise.then is the microtask, put on the microtask queue
  8. Console. log(8) is a synchronization task that is executed directly and prints 8
  9. When the main thread task completes, check for promise. then in the microtask queue
  10. SetTimeout is an asynchronous macro task. Call it timer4 and put it in the macro task queue
  11. Console. log(7) in the microtask queue is a synchronization task, executed directly, printing 7
  12. The microtask completes, and the first loop ends
  13. 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.
  14. Run timer2, console.log(3) for synchronization and print 3
  15. Check that there are no microtasks. The second Event Loop ends
  16. Run timer4, console.log(6) for synchronization and print 6
  17. Check that there are no microtasks. The third Event Loop ends
  18. Perform timer3, console.log(5) synchronization and print 5
  19. Check that there are no microtasks. The fourth Event Loop ends
  20. Perform timer1, console.log(2) synchronization and print 2
  21. 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

  1. The whole script enters the main thread as the first macro task, encounters console.log, and prints script start

  2. When a setTimeout is encountered, its callback function is dispatched to the macro task Event Queue

  3. 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

  4. 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.