This is the 7th day of my participation in the August Wenwen Challenge.

preface

What is the browser event loop? What are microtasks macro tasks? Why distinguish between micro tasks and macro tasks? You’re given a piece of code to write down the sequence of execution and so on. If you don’t know or don’t know, this article takes you through the browser event loop.

Why is there an event loop?

As we all know, JavaScript is a single-threaded interpreted scripting language, and single-threaded means that JS code is executed with only one main thread to handle all tasks.

So why isn’t JS designed to be multithreaded like Java? Because JS is designed to interact with browsers, it is a scripting language. If it’s multithreaded, imagine if, at the same time, one thread is manipulating a DOM, changing its style, and another thread is manipulating the DOM, deleting it. Who does the browser listen to?

Is there a downside to single threading? There must be. Let’s also imagine that on the main thread we have two operations A and B, A first and B second. If A takes 0.2s, there must be no problem. What if A takes 20s, 200s and 2min? Then operation B will always be in a waiting state, and this result must be unacceptable. To solve the blocking problem caused by a single thread, browsers introduced event loops.

What is an event loop?

JS tasks are divided into synchronous tasks and asynchronous tasks.

  • Synchronization task: Tasks that are immediately executed are queued on the main thread. Tasks that are immediately executed can be executed only after the first task is executed. There are mainly Promise (then/catch/finally, etc.), Object.obsever, MutationObsever, etc.
  • Asynchronous tasks:Asynchronously executed tasks do not enter the main thread, but register callback functions after the asynchronous task has a resultTask queueRead execution while waiting for the main thread to be idle. Mainly include:SetTimeout, setInterval, I/O, UI renderingEtc.

Synchronization tasks are executed in the main thread, forming an execution stack. In addition to the execution stack of the main thread, there is also a task queue, which is used to store callback functions registered after the completion of the asynchronous task.

Execution stack Performs synchronous tasks. When asynchronous tasks are encountered, asynchronous tasks are processed asynchronously and skipped. After all synchronous tasks are executed, the callback function of asynchronous tasks is pushed to the task queue. This process is repeated in a loop, which is called an event loop.

Here’s an example:

console.log("script start"); SetTimeout (() => {console.log(" I am an asynchronous task "); }, 100); console.log("script end");Copy the code

SetTimeout is an asynchronous task, so it is skipped first. After its asynchronous processing is complete, the callback is put into the task queue, and the main thread executes the callback.

Print result:

So,Asynchronous tasksWhat are they? What about asynchronous processing?

Microtasks and macro tasks

Asynchronous tasks are also different. They are divided into microtasks and macrotasks, and their execution priorities are also different.

When executing a piece of code, synchronous tasks are executed first, and when encountering asynchronous tasks, distinguish between macro and micro tasks.

If it is a macro task, the macro task is placed in a message queue and suspended for further execution.

If it is a microtask, the microtask is placed in a queue of microtasks in the global execution context and executed further down.

After all the synchronization tasks are executed, the system reads the microtask execution in the microtask queue first. After the microtask queue is cleared, the system performs the macro task in the message queue and enters the next event cycle.

Flow chart:

Each event loop, when the synchronization tasks in the execution stack have been completed, will check the microtask queue, if there are microtasks waiting to be executed, the microtasks will be executed in a first-in, first-out manner. Wait until the microtask queue is empty, then read the macro task in the message queue and enter the next event cycle. (Micro tasks are prioritized over macro tasks)

Why distinguish between microtasks and macro tasks?

Imagine if there were only one asynchronous task in a broad sense, with no distinction between micro and macro tasks.

Suppose you have an asynchronous task A and an asynchronous task B inside it. When all synchronous tasks are complete, asynchronous task A is invoked and another asynchronous task B is found. At this point, asynchronous task B is suspended for the next event loop because it does not distinguish between asynchronous tasks. This is obviously not reasonable, since it is possible that a later state will require asynchronous task B, and your goal is to complete both asynchronous tasks in this event loop.

Take a real life example. You go to the canteen to wait in line for your meal, and see yourself as an asynchronous task. When you finally get to the line, you finish your meal and need to pay by credit card. Credit card payment is also an asynchronous task, and if there is no distinction between micro tasks and macro tasks, you have to go back to the queue and wait until you are in line again before you can pay. This is clearly unreasonable.

Therefore, we need to distinguish between micro tasks and macro tasks, and form a relationship with priority, just to make it easier to jump the queue. In an event loop, the higher priority asynchronous task (queue jumping) is executed before the lower priority asynchronous task is executed.

To a chestnut

Now that we understand the browser’s event loop, let’s take a look at an interview question.

Let’s see what it prints after executing:

console.log("script start"); new Promise((res) => { console.log(1); res(); }). Then (() => {console.log(" microtask 1"); }); const p = new Promise((res) => { console.log(9); res(); }). Then (() => {console.log(" microtask 2"); }); async function foo() { console.log("async start"); await p; console.log("async end"); } foo(); SetTimeout (() => {console.log(" macro task 1"); new Promise((res) => { console.log(3); res(); }). Then (() => {console.log(" microtask 3"); }); }, 101); SetTimeout (() => {console.log(" macro task 2"); new Promise((res) => { console.log(2); res(); }). Then (() => {console.log(" microtask 4"); }); }, 100); console.log("script end");Copy the code
  1. Start with the whole code as oneMacro taskExecute, synchronize tasksconsole.logEnter the execution stack, execute the printing operation after the execution stack, printscript start.

  1. Go to the first onePromise,PromiseIs a microtask, but its constructor entry is a synchronization task,console.log(1)Go to the execution stack and print one1After that pops up the execution stack. And then we callResolve functionAnd thePromise.thenIt was a microtask, so it went inMicrotask queueIn the middle, temporarily suspended, and thenResolve functionThe execution stack is displayed.

// New Promise is a synchronization task new Promise((res) => {console.log(1); res(); })Copy the code
  1. Go to the secondPromiseMiddle, the previous onePromiseSo let’s print one9Call again,Resolve function, hang,Promise.thentoMicrotask queueIn the.

  1. performfoo()Delta function, it’s a delta functionasyncFunction (Click here to learn about Async), execute firstconsole.logPrint. Here we goawait pAfter phi, phi is equal to phinew Promise(()=>{p})Asynchronous to synchronous, but nothing happens because P has already executed, but it puts the following code in, rightnew Promise(()=>{p}).then, resulting inconsole.log("async end")Has been pushedMicrotask queueIn the.

  1. Then performsetTimeoutIt’s aMacro taskSo put it inThe message queueThe hung.

  1. Ditto. Suspend the second onesetTimeout.

  1. performconsole.log("script end").

  1. At this point, the synchronization of this code is done, and nowMicrotask queueThree of the microtasks are suspended,The message queueTwo macro tasks are pending in. As I mentioned earlier,Microtasks take precedence over macro tasks, so it is executed firstMicrotask queueThe order of tasks is first in first out (queue), so it is printed and emptied in sequenceMicrotask queueAt this point, the first cycle of events ends and the second cycle begins.

  1. inMicrotask queueWhen it’s empty, it goesThe message queueperformMacro taskNow there are two macro taskssetTimeout, which should be implemented? Is it the one that executes 101ms first on a first-in-first-out queue? No,setTimeoutIt’s going to do it based on time, so it’s going to do it first, so it’s going to do the 100ms one, and then it’s going to do the 101ms one. So let’s go to the 100ms firstsetTimeoutIn, print successivelyMacro task 2,2, after hanging inside the microtask.

  1. Then perform microtasks and wait to emptyMicrotask queueAfter that, execute 101ms as abovesetTimeout, the final print result:

  1. Let’s go to the browser and print and see what it is.

conclusion

Browser event loop mechanism is a frequent problem in the interview, I believe this article will give you more or less help, if you read this article, to help you, also please click a like, a little attention, I wish you a happy life.