background

Event loop is a very important concept, which essentially refers to the running mechanism of the computer. JavaScript language adopts this mechanism. As we all know that JavaScript is single-threaded, why is it designed to be single-threaded? In fact, a few years ago, Ruan Yifeng teacher gave the answer, such benefits improve efficiency, do only one thing at a time. However, this also leads to a problem: all tasks need to be queued, and only the previous task can be executed. The designers of JavaScript language realized that this was not possible, so they divided all tasks into two types: synchronous tasks and asynchronous tasks maintain a task queue, and the main thread reads tasks from the task queue. The whole process is a continuous loop, which is called Event loop.

Why do we need to know about it

In the real world, knowing Event loop will help you analyze an asynchronous sequence problem, but it will also help you understand the internal mechanics of browsers and Nodes, and most importantly, it is a guaranteed interview question.

Browser implementation

The main tasks in the browser are divided into two types: synchronous tasks and asynchronous tasks.

Asynchronous tasks: Macrotask and Microtask. The tasks in the macro and Microtask queue alternate with each other as the tasks are pushed, pushed, queued and queued. To get an idea of this concept, use a pseudo-code:

// Task queue (first in first out)
let EventLoop = []; 
let event;

// Execute "forever"
while (true) {
  / / a tack

  if (EventLoop.length > 0) {
    // Get the next event in the queue

    event = EventLoop.shift();

    // Now execute the next event
    try {
      event();
    } catch (error) {
      // Report an errorreportError(error); }}}Copy the code
Common MacroTasks
  • The script tag
  • setTimeout
  • setInterval
  • SetImmediate (In Node environment)
  • requestAnimationFrame
Microtasks
  • Process.nexttick (in Node environment)
  • Promise callback includes :()
  • MutationObserver

Once you know the concept, let’s look at a simple example to start with. You don’t have to know the final print result, you should be aware of the current code which are macro tasks, micro tasks

Chestnut 🌰

console.log('start');  / / no. 1

setTimeout(function () {  / / no. 2
  console.log('timeout');
}, 0);

Promise.resolve().then(function () { / / no. 3
  console.log('promise');
});

console.log('end'); / / no. 4

Copy the code

Implementation process:

Process:

  1. The runtime recognizes the log method, pushes it, and then performs inputstartOut of the stack
  2. Identify methods whose setTimeout is asynchronous (Macro task), put the anonymous callback function in the queue (macro task) and execute it in the next event loop.
  3. Runs intopromise callback, belong to (Micro tasks), placed in the (microtask) queue.
  4. The runtime recognizes the log method, pushes it, and then performs inputendOut of the stack.
  5. When the stack is empty after the main process finishes executing, the item at the head of the queue is taken out and printedpromise, until there is no data in the queue
  6. Loop through the next queue and print out on a first-in, first-out basistimeout

Types of tasks

  • Number 1: Synchronization task
  • Number 2: macro task
  • Number 3: Microtasks
  • Number 4: Synchronization task

Results of execution:

start
end
promise
timeout
Copy the code

Strike while the iron is hot

console.log('start');  / / no. 1

new Promise(function(resolve, rejected){

    console.log('Promise-1')  / / no. 2

    resolve()

}).then(function(res){  / / no. 3

    console.log('Promise-2')})setTimeout(function () {  / / no. 4
  console.log('timeout');
}, 0);

Promise.resolve().then(function () { / / no. 5
  console.log('promise');
});

console.log('end'); / / no. 6
Copy the code

Implementation process:

The only difference between this example and the previous one is the addition of a new Promise (number 2) which prints console.log(‘ promise-1 ‘). Note that only the Promise callback is for asynchronous tasks. But inside a function is a synchronization task, and that’s where most people get confused.

Results:

start
Promise-1
end
Promise-2
promise
timeout
Copy the code

Unlock the Event Loop completely

console.log('1');

async function foo() {
    console.log('13');
    await bar();
    console.log('15');
}

function bar() {
    console.log('14');
}

setTimeout(function () {
    console.log('2');
    new Promise(function (resolve) {
        console.log('4');
        resolve();
    }).then(function () {
        console.log('5');
    });
});

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

setTimeout(function () {
    console.log('9');

    new Promise(function (resolve) {
        console.log('11');
        resolve();
    }).then(function () {
        console.log('12');
    });
});

foo();
Copy the code

Implementation process:

First event loop:

Parsing the entire JavaScript file is in a macro task that is printed directly when encountered with synchronization console.log(‘1’). If function is not called, it will skip to the first setTimeout and Queue macro1. Then parse to the new Promise and execute the console.log(‘7′) code inside, then plug into the (microtask)Queue and mark micro1, After that, setTimeout is inserted again into the Queue labeled macro2, and finally foo() is promoted to async, which simply indicates that the current function is asynchronous. Does not affect function execution console.log(’13’), encountered awiat bar execute bar console.log(’14’), aWIat with block after the code, put microtask list to mark micro2;

Current macro task:

  • The synchronization code executed is:[1, 7, 13, 14]
  • Micro task Queue:[8, 15]
  • Macro task Queue:[macro1, macro2]

The input results are 1, 7, 13, 14, 8, and 15

Empty the current microtask queue at this pointmicro = []


Second event loop: The first setTimeout output console.log(‘2’) is executed, the new promise is entered into console.log(‘4’), and then is re-marked in micro as micro1. Output console.log(‘5’) to check that there are no other microtasks

Current macro task:

  • The synchronization code executed is:[2, 4]
  • Micro task Queue:[5]
  • Macro task Queue:[macro2]

The input results are 2, 4, and 5

Empty the current microtask queue at this pointmicro = []


Third event loop: Same order as the previous event loop

Current macro task:

  • The synchronization code executed is:[9, 11]
  • Micro task Queue:[12]
  • Macro task Queue:[]

The input results are 9, 11, and 12

Clear all task queues

conclusion

It is important to distinguish between macro tasks and micro tasks in the event cycle. Knowing the sequence is the only way to know the order in which the current events will be executed.