Introduction: Why is there an event loop

Important: javascript has been a single-threaded, non-blocking scripting language since its inception

Let’s talk about these two features of JavaScript:

  • Single thread:

JavaScript is single-threaded. Single-threaded means that the JavaScript engine has only one thread (the main thread) that parses and executes JavaScript code and can only do one thing at a time. Single-threaded existence is inevitable. In a browser, if javascript is multi-threaded, it is contradictory when two threads simultaneously perform an operation on the DOM, such as adding an event to it while the other removes the DOM

  • Non-blocking:

When our Javascript code runs an asynchronous task (Ajax, etc.), the main thread suspends the task and executes the callback function based on the result when the asynchronous task returns

How do you make it non-blocking? That’s where we need our main character, the Event Loop.

Event loops in the browser

Let’s look at a classic diagram that basically encapsulates the Event Loop. (From the talk — Philip Roberts: What exactly is an Event Loop? | behind Europe JSConf 2014) demonstrate written in Loupe is also the speaker ((Loupe is a visualization tool, can help you understand the JavaScript call stack/event loop/callback queue how to interact with each other))

When javascript code executes, different variables are stored in different locations in memory: in the heap and in the stack. There are objects in the heap. The stack holds basic type variables and Pointers to objects

Call Stack: When we call a method, js generates a context that corresponds to the method. The execution environment contains the method’s private scope, the reference to the upper scope, the method’s parameters, the variables defined in that scope, and the this object in that scope. When a series of methods are called in turn, since JS is single-threaded and can only execute one method at a time, the methods are queued in a separate place. This place is called the execution stack

For example, here is an execution of synchronous code

function a() {
    b();
    console.log('a');
}
function b() {
    console.log('b')
}
a();
Copy the code

Let’s demonstrate the execution process of the code through Loupe:

  • Execute the function a() to push first
  • In a(), function b() is executed first and function b() is pushed
  • Execute function b(), console.log(‘b’) to push
  • Output b, console.log(‘b’) off the stack
  • Function b() completes its execution, leaving the stack
  • Console. log(‘a’) pushes, executes, outputs a, and exits the stack
  • Function A completes, and the stack is off

The execution of synchronous code is relatively simple, but what about asynchronous execution?

Callback Queue: Instead of waiting for an asynchronous event to return, the JS engine suspends the event and continues to execute other tasks in the stack. When an asynchronous event returns a result, JS will add the event to a different queue from the current execution stack, which is called the event queue

Is put into the event queue is not immediately implement the callback, but wait for the current execution stack of all tasks are completed, the main thread idle state, the main thread to find whether there is a task in the event queue, if you have, then remove the top of the list events, and put the corresponding callback event in execution stack, and then perform the synchronization code

Here’s an example from Loupe officials:

$.on('button'.'click'.function onClick() {
    setTimeout(function timer() {
        console.log('You clicked the button! ');    
    }, 2000);
});

console.log("Hi!");

setTimeout(function timeout() {
    console.log("Click the button!");
}, 5000);

console.log("Welcome to loupe.");
Copy the code

Let’s analyze the execution process:

  • First of all, you register a click event, execute it asynchronously, and then you put it inWeb Api
  • console.log(“Hi!” ), directly execute, output Hi
  • performsetTimeout, asynchronously, to mount it
  • Run console.log(“Welcome to loupe.”) to output Welcome to loupe.
  • Five seconds later,setTimeoutExecute the callback, put the callback into the event queue, and once the main thread is idle, fetch and run
  • I clicked the button (I only did it once here), triggered the click event, put the callback to the click event into the event queue, and once the main thread is idle, take it out and run
  • Run in the click event callbacksetTimeout
  • After 2 seconds,setTimeoutExecute the callback, put the callback into the event queue, and once the main thread is idle, fetch and run

When you look back at this picture, it should give you a sense of clarity

The above process works like this. Queue.waitformessage () waits synchronously for messages to arrive (if no messages are currently waiting to be processed), so we call it an Event Loop.

while (queue.waitForMessage()) {
  queue.processNextMessage();
}
Copy the code

Microtasks and macro tasks

Micro – Micro – a Task

Common micro-tasks: new Promise().then(callback), MutationObserve, and so on (async and await) are actually syntactic candies for promises

Macro – Macro – a Task

Common macro-Tasks: setTimeout, setInterval, script (overall code), I/O operations, UI interaction events, postMessage, etc

The execution order of the event loop

The result of the asynchronous task is put into an event queue. Depending on the type of asynchronous event mentioned above, the event is actually put into the corresponding macro and microtask queues

The process of Eveent Loop is as follows:

  • Execute a macro task (usually starts with the overall code (script), if no optional macro task is available, the microtask is processed directly
  • If a microtask is encountered during execution, it is added to the task queue of the microtask
  • If a macro task is encountered during execution, it is added to the task queue of the macro task
  • After executing a macro task, it is necessary to check whether there are any tasks in the microtask queue that need to be executed. If there are, all tasks will be executed; if there are no tasks, go to the next step
  • Check the render and thenGUIThe thread takes over the rendering and renders the browser
  • After rendering, the JS thread continues to take over, starting the next macro task… (Loop through the steps above)

As shown in the figure below:

Summary of execution sequence: execute the macro task, and then execute the microtask generated by the macro task. If a new microtask is generated during the execution of the microtask, the microtask will continue to execute. After the execution of the microtask is completed, it will return to the macro task for the next round of cycle

To understand this better, let’s look at an example

console.log('start')

setTimeout(function() {
  console.log('setTimeout')},0)

Promise.resolve().then(function() {
  console.log('promise1')
}).then(function() {
  console.log('promise2')})console.log('end')
Copy the code

Let’s break it down:

  • The global executionscriptOutput the start,
  • performsetTimeoutPush themacrotaskThe queue,promise.thenBack into themicrotaskQueue, and finally executeconsole.log('end')And the outputend
  • globalscriptIt’s a macro task, it’s done and then it’s donemicrotaskQueue task, executepromiseThe callback to printpromise1
  • promiseThe callback function returns by defaultundefined.promiseState changes tofullfillTrigger the followingthenCall back and continue pressing inmicrotaskThe queue,event loopWill put the currentThe microTask queue continues to execute until the second task is executedPromise. then ‘prints promise2
  • At this momentmicrotaskThe queue is empty, and then the main thread is going to do somethingUIRender work (not necessarily done), then start the next roundevent loop, the implementation ofsetTimeoutCallback to print outsetTimeout

Therefore, the final results are as follows:

start
end
promise1
promise2
setTimeout
Copy the code

exercises

Increase the environment is that the written test will be out of the event loop interview, now actually may be more difficult than the example above, the reason is that the task and macro task involves a lot of knowledge points, this needs us to further consolidate the basis of our knowledge, I believe that can seriously the following topic, to be able to better grasp the event loop

I will not do the analysis, you can not understand the question in the comments section together to communicate

Subject to a

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

new Promise(function(resolve, reject) {
    console.log('children4');
    setTimeout(function() {
        console.log('children5');
        resolve('children6')},0)
}).then((res) = > {
    console.log('children7');
    setTimeout(() = > {
        console.log(res);
    }, 0)})Copy the code
Click to see the answer

start children4 children2 children3 children5 children7

Topic 2

const p = function() {
    return new Promise((resolve, reject) = > {
        const p1 = new Promise((resolve, reject) = > {
            setTimeout(() = > {
                resolve(1)},0)
            resolve(2)
        })
        p1.then((res) = > {
            console.log(res);
        })
        console.log(3);
        resolve(4);
    })
}


p().then((res) = > {
    console.log(res);
})
console.log('end');
Copy the code
Click to see the answer

3 end 2 4

Topic 3

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
Click to see the answer

script start async1 start async2 promise1 script end async1 end promise2 setTimeout

Topic 4

let resolvePromise = new Promise(resolve= > {
  let resolvedPromise = Promise.resolve()
  resolve(resolvedPromise);
  // Resolve (resolvedPromise)
  // Promise.resolve().then(() => resolvedPromise.then(resolve));
})
resolvePromise.then(() = > {
  console.log('resolvePromise resolved')})let resolvedPromiseThen = Promise.resolve().then(res= > {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() = > {
    console.log('promise2')
  })
  .then(() = > {
    console.log('promise3')})Copy the code
Click to see the answer

promise1 -> promise2 -> resolvePromise resolved -> promise3

Topic 5

console.log('script start');

setTimeout(() = > {
  console.log('Gopal');
}, 1 * 2000);

Promise.resolve()
.then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});


async function foo() {
  await bar()
  console.log('async1 end')
}
foo()

async function errorFunc () {
  try {
    / / Tips: implicit reference: https://zh.javascript.info/promise-error-handling: a try... catch
    // The promise.reject () method returns a Promise object with a reason for rejecting it
    // Promise.reject('error!!! ') === new Error('error!!! ')
    await Promise.reject('error!!! ')}catch(e) {
    console.log(e)
  }
  console.log('async1');
  return Promise.resolve('async1 success')
}
errorFunc().then(res= > console.log(res))

function bar() {
  console.log('async2 end')}console.log('script end');
Copy the code
Click to see the answer

script start async2 end script end promise1 async1 end error!!! async1 promise2 async1 success Gopal

Topic 6

new Promise((resolve, reject) = > {
  console.log(1)
  resolve()
})
.then(() = > {
  console.log(2)
  new Promise((resolve, reject) = > {
      console.log(3)
      setTimeout(() = > {
        reject();
      }, 3 * 1000);
      resolve()
  })
    .then(() = > {
      console.log(4)
      new Promise((resolve, reject) = > {
          console.log(5)
          resolve();
        })
        .then(() = > {
          console.log(7)
        })
        .then(() = > {
          console.log(9)
        })
    })
    .then(() = > {
      console.log(8)
    })
})
.then(() = > {
  console.log(6)})Copy the code
Click to see the answer

One, two, three, four, five, six, seven, eight, nine

Topic 7

console.log('1');

setTimeout(() = > {
  console.log('2');
  Promise.resolve().then(() = > {
    console.log('3');
  })
  new Promise((resolve) = > {
    console.log('4');
    resolve();
  }).then(() = > {
    console.log('5')})})Promise.reject().then(() = > {
  console.log('13');
}, () = > {
  console.log('12');
})

new Promise((resolve) = > {
  console.log('7');
  resolve();
}).then(() = > {
  console.log('8')})setTimeout(() = > {
  console.log('9');
  Promise.resolve().then(() = > {
    console.log('10');
  })
  new Promise((resolve) = > {
    console.log('11');
    resolve();
  }).then(() = > {
    console.log('12')})})Copy the code
Click to see the answer

1, 7, 12, 8, 2, 4, 9, 11, 3, 5, 10, 12

conclusion

This article from the TWO characteristics of JS: Single threading and non-blocking introduces the need for event loops because event loops behave very differently in the browser and node.js. I’ll only talk about event loops in the browser, and I’ll introduce microtasks and macro tasks, and how they execute. I’ll finish with seven questions to help you reinforce your knowledge

If you like, don’t forget to like ~ ~

Recommended excellent articles in the past

  • 20 Vue skills that a qualified intermediate front end engineer should master
  • 【Vue Advanced 】 — How to implement transparent transmission of component properties?
  • What the Front end should Know about HTTP
  • The most powerful CSS layout – Grid layout
  • How to write a complete Vue application in Typescript
  • Web debugging tool the front end should know about — Whistle

reference

Detail the Event Loop mechanism in JavaScript

In-depth understanding of NodeJS event loops

Concurrency models and event loops

【 Front end system 】 Talk about understanding EventLoop from an interview question

Philip Roberts: What exactly is an Event Loop? | JSConf 2014 in Europe

Event Loop mechanism in JavaScript

JS event loop mechanism (Event loop) macro task/micro task

Understanding the js event loop (browser)

JS event loop and Macro Micro task queue from interview questions