1

JavaScript single-threaded origin:

JavaScript is a browser scripting language. The main purpose of JavaScript is to interact with users and manipulate the DOM. In order to avoid complexity, JavaScript is a single-threaded language since its birth.
For example, assuming that JavaScript has two threads at the same time, one thread adds content to a DOM node, and the other thread deletes this node, which thread should the browser take? So, to avoid complexity, JavaScript has been single-threaded since its inception.

Single thread and existing problems:

Single-threaded means that all tasks need to be queued, and the next task will be executed after the previous task ends. If the former task takes a long time, the latter task has to wait forever.

Problem Solving - Synchronous, Asynchronous

synchronous (synchronous tasks) and asynchronousk (asynchronous tasks)

  • A synchronous task is a task that invokes an immediate result . A synchronous task is queued for execution on the main thread . The latter task can only be executed after the execution of the previous task is completed.
  • Asynchronous tasks are tasks that cannot be called immediately and require additional operations to expect results. Asynchronous tasks do not enter the main thread, but enter the " task queue " (task queue), only the "task queue" notifies the main thread, When an asynchronous task can be executed, the task will enter the main thread for execution.

macro-task and micro-task

  • Macro task: macro-task It can be understood that the code executed each time the execution stack is a macro task (including each time an event callback is obtained from the event queue and placed on the execution stack for execution, each macro task will be executed from beginning to end. After the task is executed, other will not be executed) including the overall code script, setTimeout, setInterval, etc.
    image.png
  • Micro-task: micro-task can be understood as tasks that are executed immediately after the execution of the current task, including Promise, process.nextTick, etc.
    image.png

    Event Loop

    Event Loop is an event loop, which refers to a mechanism of browsers or Node that solves the problem of non-blocking when running a single thread of JavaScript. In fact, the execution of macro task queues and micro task queues is part of the event loop.
    The specific process of the event loop is as follows:

  1. From the macro task queue, according to the order of entry, find the first macro task to be executed, put it into the call stack, and start execution;
  2. After all synchronization tasks under the macro task are executed, that is, after the call stack is emptied, the macro task is pushed out of the macro task queue, and then the micro task queue starts to execute the micro tasks in the order of entry until the micro task queue is emptied;
  3. When the microtask queue is emptied, an event loop ends;
  4. Then from the macro task queue, find the next macro task to be executed, and start the second event loop until the macro task queue is emptied.

Js execution mechanism flow chart

Js执行机制流程图
Notice:

  • When we execute it for the first time, the interpreter will put the whole code script into the macro task queue, so the event loop starts from the first macro task; so I separate the main thread in the flowchart.
  • If a new microtask is generated and added to the microtask queue during the execution of the microtask, it also needs to be emptied together; the next macrotask will not be executed until the microtask queue is emptied.

Flowchart explanation:

  1. After the Js task is pushed into the stack, it is judged whether it is a synchronous task, and the synchronous task is executed immediately; if it is an asynchronous task, it is judged that the micro task or macro task is added to the corresponding queue for processing.
  2. After all synchronous processes are executed, all microtasks in the current microtask queue are executed immediately (because the overall Js code is regarded as a macro task)
  3. When the micro-queue is emptied, execute the next macro-task in the macro-task queue. After the execution is over, determine whether there are micro-tasks in the micro-task queue. If so, clear the micro-task queue and start the next cycle.

Event Loop Exercises

 console.log("a");

setTimeout(function () {
    console.log("b");
}, 0);

new Promise((resolve) => {
    console.log("c");
    resolve();
})
    .then(function () {
        console.log("d");
    })
    .then(function () {
        console.log("e");
    });

console.log("f");

/**
* 输出结果:a c f d e b
*/
 async function async1() {
    console.log("a");
    const res = await async2();
    console.log("b");
}
async function async2() {
    console.log("c");
    return 2;
}
console.log("d");
setTimeout(() => {
    console.log("e");
}, 0);
async1().then(res => {
    console.log("f")
})
new Promise((resolve) => {
    console.log("g");
    resolve();
}).then(() => {
    console.log("h");
});
console.log("i");
/**
* 输出结果:d a c g i b h f e 
*/

This code declares two async methods,

  1. In the overall Script statement, first output: d ;
  2. The statement in setTimeout is added to the macro task queue;

    macro task queue microtask queue
    Overall Script
    setTimeout
  3. async1() executes, output: a ; after async2() executes, output: c ; await blocks execution; console.log ("b") does not execute for the time being, but executes the main thread task;
  4. At this time, async1() is not executed, and the .then() method will not be added to the microtask queue;

    macro task queue microtask queue
    Overall Script
    setTimeout
  5. Execute new Promise output: g ; .then(() => {console.log("h");}) join the microtask queue;

    macro task queue microtask queue
    Overall Script console.log("h")
    setTimeout

    6. Execute the console.log("i"); statement, output: i ; At this time, all the synchronization tasks of the main thread have been executed, and return to the block to continue execution; output: b ; .then(res => {console. log("f")}) join the micro queue;

    macro task queue microtask queue
    setTimeout console.log("h")
    console.log("f")

    7. After clearing the micro-task queue, execute the next task of the macro-task queue and start the next cycle; output h , f , e ;

 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('7');
    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')
    })
})
/**
* 输出结果:1,7,6,8,2,4,3,5,9,11,10,12(node与浏览器可能略有不同)
*/

Note:

  • In node, process.nextTick() has higher priority than Promise.then, catch, finally;
  • In node, macro task queue: setImmediate() queue priority is higher than setTimeout() queue;
  • After node14, every time a macro task is executed, the instant micro task queue is emptied.

Reference documentation:


很白的小白
145 声望125 粉丝