Event loop

Simple concept, it is an endless loop in the JavaScript engine between waiting for a task, executing a task, going to sleep, waiting for more tasks.

The general algorithm of the engine:

  1. When there is a task:
  • Start with the task that comes in first.
  1. Hibernate until a task appears, then go to Step 1.

Set up tasks — the engine handles them — and then wait for more tasks (that is, sleep, consuming little CPU).

When a task arrives, the engine may be busy and the task will be queued.

Multiple tasks form a queue, known as a “macro task queue” (V8 terminology) :

Tasks in the queue are executed on a first-in, first-out basis.

Macro and micro tasks

Macro and micro tasks are asynchronous tasks and belong to a queue.

Macro tasks: overall code, setTimeout, setInterval, I/O operations, UI rendering…

New Promise().then/.catch/.finally, process.nexttick, MutainOberver..

After each macro task, the engine immediately executes all tasks in the microtask queue, and then executes any other macro task, or render, or anything else. The flow chart can be viewed in macro task/micro task of JS Event loop.

For example:

setTimeout(() = > alert("timeout"));

Promise.resolve()
  .then(() = > alert("promise"));

alert("code");
Copy the code

The order of execution here is:

  1. Code is shown first because it is a regular synchronous call.
  2. Promise comes second, because then passes through the microtask queue and executes after the current code.
  3. Timeout is displayed last because it is a macro task.

Here’s another classic interview question:

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');
Copy the code

Vernacular analysis:

From the top, execute the first macro task as a whole:

Async1 and async2 are defined but not called. Go down to the whole code block and print script start.

SetTimeout needs to wait for all messages in the current queue to be processed before it can be executed. The article refers to – zero delay, so it is put into the next macro task.

Next async1() is executed, printing ‘async1 start’; Await async2() =>{async2()} new Promise(()=>{async2()}))

New Promise(()=>{async2(); }). Then (()=>{console.log(async1 end); // Then, which belongs to microtasks})Copy the code

Next, you get a new Promise, which is a normal task and outputs promise1;

Next, print script end.

At this point, the first macro task is completed, and then the microtask queue is cleared:

First microtask: output async1 end;

The second microtask: promise2 at this point, the microtask is completed and the second macro task is enabled: setTimeout is output.

/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
Copy the code

Detailed analysis to participate: a simple explanation of the JavaScript event loop from a problem

Let’s do another one:

console.log('start'); 

setTimeout(() = > {
    console.log('children2');
    Promise.resolve().then(() = >{
        console.log('children3'); })},0);

new Promise(function (resolve, reject) {
    console.log('children4'); / / macro task
    // The first round of macro tasks ends, and an attempt is made to empty the microtask queue. Found no microtask because resolve('children6') was placed in setTimeout
    setTimeout(() = > {
        console.log('children5');
        resolve('children6') // Add microtask delay with setTimeout
    }, 0);
}).then((res) = >{
    console.log('children7');
    setTimeout(() = > {
        console.log(res);
    }, 0);
})
Copy the code

Output:

start
children4
children2
children5
children3
children7
children6
Copy the code

The flow of the event loop mechanism:

  1. The main thread executes the whole JavaScript code and forms the execution context stack. When encountering various task sources, the asynchronous task specified by the main thread is suspended. After receiving the response result, the asynchronous task is put into the corresponding task queue until the execution context stack only has the global context.
  2. All task queues in the microtask queue are pushed and executed according to priority and asynchronous tasks in a single task queue are pushed and executed in a first-in-first-out manner until all microtask queues are emptied.
  3. The asynchronous tasks in the macro task queue with the highest priority in the task queue are pushed and executed in first-in first-out mode.
  4. Repeat steps 2 and 3 until all macro and micro task queues are empty and the global context is unloaded.

In simple terms: the main thread executes the whole code block first, distributes the tasks specified by each task source encountered in the process to each task queue, and then pushes the microtask queue and macro task queue alternately until all the task queues are cleared and the global context is pushed out of the stack.

see

  • Event loops: microtasks and macro tasks
  • Microtasks
  • Let’s talk about JavaScript event loops in a nutshell