Reference article:

  • 10 minutes to understand the execution mechanism of JS engine
  • JS event loop mechanism (Event loop) macro task/micro task
  • The Event Loop is an Event Loop
  • Common asynchronous pen test questions

The first two things to know

  • JavaScript is a single-threaded language
  • An Event Loop is an execution mechanism for JavaScript

How can a single thread achieve asynchrony?

Through an event loop

event loop

Js is single-threaded and can be divided into two types of tasks:

  • Synchronization task
  • Asynchronous tasks

Let’s start with a piece of code:

console.log('script start'); // Synchronize tasks

setTimeout(function() {  // The asynchronous task is executed 0 seconds after the synchronization task is completed
  console.log('setTimeout');
}, 0);

//Promise's.then() function is an asynchronous task
Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end'); // Synchronize tasks
Copy the code

Script start, script end, promise1, promise2, setTimeout

Why in this order? As shown in figure:

Reading:

  • The synchronous task enters the main thread, and the asynchronous task enters the Event Table and registers the function
  • When the specified thing is done, the Event Table moves the registered callback function to the Event Queue
  • After the synchronization task enters the main thread, it will be executed until the main thread is idle. Then the Event Queue will be checked to see if there are any asynchronous tasks that can be executed. If there are asynchronous tasks, they will be pushed to the main thread
  • The above process is repeated, i.e., Event Loop;

Microtasks, macrotasks?

First of all, microtasks and macro tasks are asynchronous tasks. They all belong to the same queue. The main difference is the order in which they are executed

  • Macro-task: Includes the overall code script, setTimeout, setInterval, and setImmediate
  • Micro-task: Native Promise(some implemented Promises put then methods in macro tasks), Process. nextTick, MutationObserver

Comprehensive classification:

  • Execute a macro task, and if a microtask is encountered during the process, put it in the event queue of the microtask
  • After the execution of the current macro task is completed, the event queue of microtasks will be viewed and the global microtasks will be executed in turn

Lots of chestnuts

Chestnut 1:

// Synchronize the task and put it in the main thread
console.log(1); 

// The asynchronous task is placed in the event table and 0 seconds later is pushed to the Event queue
setTimeout(function () {
    console.log(2)},0);

// Synchronize the task and put it in the main thread
console.log(3); 
Copy the code

So print order: 1,3,2

Chestnut 2:

 setTimeout(function(){
     console.log('Start timer')});new Promise(function(resolve){
     console.log('Execute for loop now');
     for(var i = 0; i < 10000; i++){
         i == 99 && resolve();
     }
 }).then(function(){
     console.log('Execute the THEN function')});console.log('End of code execution');
Copy the code
  1. If you encounter setTimeout, put it in the event queue of the macro task, i.emacrotasks=['setTImeout']
  2. Execute the new Promise directly, output “execute the for loop immediately”, and place the callback function from the THEN method in the event queue of the microtask, i.emicrotasks=['then']
  3. Execute the synchronization task, output “End of code execution”
  4. The first round of event loop is over, and the next round of microtasks is viewed, i.emicrotasks=['then'], output “Execute then function”
  5. The second round of the event loop begins by executing a macro task, i.emacrotasks=['setTImeout'], output “Timer start”
  6. Output: Immediately execute the for loop, code execution ends, execute the THEN function, and the timer starts

Chestnut 3:

setTimeout((a)= > {
    cosnole.log('A');
},0)

var obj = {
    func: function() {
        setTimeout(function () {
            console.log('B')},0);
        return new Promise(function () {
            console.log('C');
            resolve();
        })
    }
}
obj.func().then(function() {
    console.log('D')})console.log('E')

// C E D A B
Copy the code
  1. In sequence, setTimeout A is addedMacro taskIn the event queuemacrotasks=['A'];
  2. When obj.fun() is executed, setTimeout B is addedMacro taskIn the event queuemacrotasks=['A','B'];
  3. It then returns a Promise object that is executed immediately after the Promise is created, with the output ‘C’.
  4. Then, the callback function from the THEN method is added toMicro tasksIn the event queue of, the script will be executed only after all synchronization tasks are completedmicrotasks=['C']
  5. Then execute the synchronization task and output ‘E’.
  6. When the synchronization task finishes, check the event queue of the microtask, complete the task, and output ‘D’.
  7. At the end of the macro task, the event queue will output ‘A’ and ‘B’ in the order of the time it was queued.

Chestnut 4:

console.log('1');

/ / named setTimeout1
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {  / / named process2
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() { / / named then2
        console.log('5')})})/ / named process1
process.nextTick(function() {
    console.log('6');
})

new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() { / / named then1
    console.log('8')})/ / named setTimeout2
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {  / / named process3
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() { / / named then3
        console.log('12')})})Copy the code
  1. Execute sequentially, execute the synchronization task, output ‘1’, put setTimeout1 in the macro task’s Event queue, and put Process1 in the microtask’s Event queue. namelymacrotasks = ['setTimeout1'].microtasks=['process1']
  2. When the Promise synchronization task is encountered, output ‘7’ and place the callback function from the THEN in the event queue of the microtask, i.emicrotasks=['process1', 'then1']
  3. Then we encounter setTimeout, and we put setTimeout2 in the event Queue of the macro task, i.emacrotasks = ['setTimeout1', 'setTimeout2'].
  4. At the end of the first round of event loop, output ‘1, 7’, the main thread is idle, and check whether there are tasks in the event queue of the microtaskmicrotasks=['process1', 'then1'], so output ‘6, 8’. After the task is executed, the event queue of the microtask is cleared, that ismicrotasks = [].
  5. The second round of the event loop starts by executing the macro task’s event queue in sequence, that is, setTimeout1, output ‘2’, and then putting process2 into the micro task’s Event queue, that is, the Promise output ‘4’. And put then2 in the event queue of the microtask, i.emicrotasks=['process2', 'then2']
  6. The second round of the event loop ends, the main thread is idle, and the event queue of the microtask is checkedmicrotasks=['process2', 'then2'], so output ‘3 and 5’. After the task is executed, the event queue of the microtask is cleared, i.emicrotasks = [].
  7. The third round of the event loop starts by looking at the event queue of the macro task,macrotasks = ['setTimeout2'], similarly output ‘9, 11’, at this timemicrotasks=['process3, 'then3'].
  8. At the end of the third round of the event loop, the main thread is idlemicrotasks=['process3, 'then3'], so output ’10, 12′
  9. Run results in order: 1,7,6,8,2,4,3,5,9,11,10,12

(… I feel so wordy.)

Chestnut 5:

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(function (resolve) {
    console.log('promise1');
    resolve();
}).then(function () {
    console.log('promise2');
});

console.log('script end');

// Output sequence:
// script start 
// async1 start 
// async2 
// promise1 
// script end 
// async1 end
// promise2
// setTimeout
Copy the code

In fact, await is a flag that frees the thread, and the expression after the await is executed once, adding the code after the await to a microtask, and then jumping out of the entire Async function to execute the subsequent code

async function async1 () {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

/ / equivalent to the
function async1 () {
    console.log('async1 start');
    Promise.resolve(async2()).then((a)= > {
        console.log('async1 end'); })}Copy the code
  1. Execute sequentially, execute the synchronization task, output script start, encounter setTimeout and put it in the event queue of macro task, i.emacrotasks = [setTimeout];
  2. Execute async1 method, output async1 start, execute async2(), output async2, put the statement following the await in the event queue of the microtask, i.emicrotasks = ['async1 end'];
  3. Execute Promisem, output PromisE1, and put the callback function of then into the event queue of the microtask, i.emicrotasks = ['async1 end', 'promise2']
  4. Finally execute output script end, so far the end of the first round, view microtask queue, output async1 end, promise2
  5. Microtask clears, executable macro task queue, output setTimeout