The most effective way to learn in the world is to take what you’ve learned and teach it to another person in a way that you understand. ‘

1. The Event Loop

Many people have heard that JS is single threaded, but what is a thread? What is the process? What’s the relationship between them?

In essence, both terms are descriptions of CPU time slices. Processes describe how long it takes the CPU to run instructions, load and save context. A thread is a smaller unit of a process that describes how long it takes to execute an instruction. In the browser, opening a Tab page is a process, in which UI rendering, JS code execution, DOM loading, Http request, etc., are all threads; In terms of JS code execution, a function is a process, and the definition of variables in the function, the call of other functions and other tasks are a thread.

JS execution is single threaded, which means that JS can only handle one task at a time when executing code and must be executed in queue order. The main purpose of JS is to handle front-end interactions, including manipulation of DOM nodes. If JS is multi-threaded, one thread needs to delete the DOM node while another thread needs to operate on the same DOM node during web page interaction, how can we determine which thread should be executed first? However, if there are multiple tasks in the queue, the execution of the previous task will block the next task, resulting in inefficient code execution. Like an AJAX request thread, you make a request and wait for the result of the response while the CPU is idle. In this regard, JS Event Loop mechanism (Event Loop) is a good solution to the problem.

2. Synchronous and asynchronous tasks

JavaScript divides tasks into two types: synchronous and asynchronous.

  • Synchronization task: a task that results immediately after its execution. The synchronization task is executed in the main thread, generating a stack as it executes, with complex data types (Object) in the heap and basic data types (String, Number, Boolean, Null, Undefined, Symbol) in the stack.

  • Asynchronous task: After an asynchronous task is executed, the result cannot be obtained immediately and the corresponding task must be obtained after a period of time. It is divided into Macrotask and Microtask.

Macro task: Main program Script, setTimeout, setInterval, setImmediate, I/O Operations (mouse Click, Keypress, Network Event), UI rendering, requestAnimationTrame, etc.

Microtasks: Promise, MutationObserver, process.nexttick (), mutation, Object.oberse, etc.

3. Main thread, stack, and queue

4. The Event Loop

  1. The synchronous task is pushed to the main thread and the asynchronous task is pushed to the task queue.
  2. The synchronization task in the main thread is pushed onto the execution stack to start execution.
  3. When the execution stack is empty, the micro tasks in the task queue are read and pushed to the execution stack.
  4. Loop through the microtask queue until the microtask list is empty;
  5. Read the first macro task in the macro task list and push the execution stack;
  6. The macro task is equivalent to a thread, and steps 1-5 are repeated until the task queue is empty.

Example:

const a = 1;
const arr = [1, 2, 3];
console.log('1');
console.log(a, arr);

setTimeout(function () {
    console.log('2');
    process.nextTick(function () {
        console.log('3');
    });
    new Promise(function (resolve) {
        console.log('4');
        resolve();
    }).then(function () {
        console.log('5');
    });
}, 100);

new Promise(function (resolve) {
    console.log('6');
    resolve();
}).then(function () {
    console.log('7');
});

process.nextTick(function () {
    console.log('8');
    setImmediate(() => {
        console.info('9');
    });
    new Promise(function (resolve) {
        console.log('10');
        resolve();
    }).then(function () {
        console.log('11');
    });
    setTimeout(function () {
        console.log('12');
        setImmediate(() => {
            console.info('13');
        });
        process.nextTick(function () {
            console.log('14');
        });
        new Promise(function (resolve) {
            console.log('15');
            resolve();
        }).then(function () {
            console.log('16');
        });
    }, 100);
    process.nextTick(function () {
        console.log('17'); }); }); Result: 11,[1,2,3] 6 7 8 10 17 11 9 2 4 5 3 12 15 16 13 14Copy the code

Resolution:

The above code is equivalent to a process, in which the function execution is a thread, a task.

  1. The synchronous task is pushed to the main thread and the asynchronous task is pushed to the task queue.
  2. The synchronization task in the main thread is pushed onto the execution stack to start execution. Results 1 1,[1,2,3]
  3. When the execution stack is empty, the micro tasks in the task queue are read and pushed to the execution stack. So we get 6, 7
  4. Loop through the microtask queue until the microtask list is empty; You get 8, 10, 17, 11, 9
  5. Read the first macro task in the macro task list and push the execution stack; You get 2, 4, 5, 3
  6. The macro task is equivalent to a thread, and steps 1-5 are repeated until the task queue is empty.