1 Browser Multi-threading

Let’s start with the browser, which contains multiple threads responsible for compiling code, UI rendering, network processing, reading files, and more


2 JS main thread

JS engine

If JavaScript is a drawing, then the JS engine is the machine that implements the drawing

Let me take a look at the structure of the JS engine


  • Execution stack: Where JS code executes, and when the engine encounters executable code blocks such as function calls, it pushes them onto the execution stack

  • Memory heap: The place where memory allocation occurs and where variable and function declarations are physically stored as they are encountered

The main thread

JS single thread, refers to the JS engine, parsing JS execution stack is unique, all JS code can only be executed sequentially in this stack.

JS engine this single stack parsing code thread, we call the main thread

3 JS running environment

JS run

I thought it was straight boy

JS single threading means that all tasks need to be queued until the previous task completes before the next task is executed. If the first task takes a long time, the second task

You have to wait…


It’s really sneaky

If it’s because of the amount of computation, the browser CPU is too busy, but most of the time the CPU is idle. If you encounter something like AN I/O operation (reading files slowly),

You have to wait until you’re done before you can move on.

At this point, the JavaScript designers realized that the main thread didn’t care about the I/O operations at all, and the CPU pressure point wasn’t that big anyway, so they could just put it away

Suspend, let the CPU dedicated to a thread to execute, continue to perform the following task. When it completes, store the callback it returns, and then suspend it

The mission continues.

Thus, JS tasks are largely divided into two types:

  • Synchronous task: Tasks executed directly on the main thread, in order from front to back, such as script

  • Asynchronous tasks: Time-consuming and complex operations, the main thread will be suspended and thrown to the Web API to find the corresponding thread to deal with. When the main thread is idle, the callback will be taken back to the main thread for execution, such as setTimeout, I/O, Promise…

JS runtime environment

So the actual running environment of JS is a bit more complicated, like the following


According to the processing mode of different JS tasks, the author divides into synchronous and asynchronous tasks.

The task is divided into two categories again:


Let’s look at the structure of the JS runtime environment

  • JS engine: Used to execute JS code

  • Web API: contains helper threads to handle various asynchronous tasks

  • Callback queue: A callback queue that is classified into macro task queues and micro task queues according to the first-in, first-out principle. Callback queue is used to store the callback after the execution of their asynchronous tasks

  • Event Loop: The brain of the JS running environment, in order to prevent the main thread from blocking, to determine when to call and what task to remove the main thread judgment logic

4 Browser Event Loop

Finally, the event loop, which basically means coordinating tasks (micro tasks, macro tasks) to perform sequential judgment logic.

The full name of event loop is event loop. From the loop, we can know that our judgment logic checks not only once, but all the time. Each loop operation becomes a tick, and the processing of each tick is complicated.

Key steps

  • Execute a macro task, execute it directly on the stack (yes! If you don’t have a script, go to the macro task team

  • If a microtask is encountered during execution, it is added to the microtask queue

  • After rendering is complete, perform all microtask callbacks in the microtask queue until it is empty

  • After the microtask is cleared, the GUI thread takes over the controller rendering page

  • The next cycle…

The flow chart


Are you confused at first glance? Let us vividly understand!

if

  • The entire JS – program is a project
  • Macro task – a development iteration given to us by our leader, which assigns us relevant tasks and time to complete on time
  • Microtasks – Urgent bugs that need to be fixed in each release iteration. They need to be completed as quickly as possible. All current bugs should be removed in each iteration
  • Macro Task Queue – The scheduling of version iterations
  • Microtask Queue – Bug list (Zen Tao!)

the

  • The initial version is urgent, there is no schedule, we should directly develop online (JS is an interpreted language, script is compiled and executed directly), if there are some time-consuming requirements in the development, we should put them into the schedule first, and the initial development version is completed (script execution is finished).

  • Fix all the bugs in the current Meditation path and launch urgently (clear the current microtask and end the first cycle)

  • Then take the first macro task from the queue for version iteration (take the first macro task from the macro task queue, execute)

  • After opening and sending, it is also necessary to clear all the current bugs, and then go online (after completing the macro task, it is also necessary to clear all the current micro tasks, and the second cycle ends).

  • Then move on to the next iteration… (Next cycle)

  • End the iteration until there are no more tasks scheduled, the project is stable, and the macro task queue is empty

Is this example vivid enough?

So let’s talk about a couple of things

  • After executing a macro task, you must clear all the current microtasks (including the newly generated microtask queue) before executing the next macro task

  • Page rendering after macro task execution, clean up after microtask run: macro task – microtask – Render

  • GUI rendering thread and main thread conflict, cannot execute JS and render at the same time, rendering will give control to GUI

  • Priority we say micro tasks are higher than macro tasks because we are trying to determine the priority of asynchronous tasks:

    Script (first macro task directly synchronized execution) - microtask - macro task - microtask -...Copy the code

Homework begins!

Let’s start with an easy one

console.log(1)



setTimeout((a)= > {

  console.log(2)

  Promise.resolve().then((a)= > {

    console.log(3)

  })

}, 100);



new Promise((resolve, reject) = > {

  console.log(4)

  resolve(5)

}).then(data= > {

  console.log(data)

})



setTimeout((a)= > {

  console.log(6)

}, 50);



console.log(7)

Copy the code
















See if you’re right?

   1  4  7  5  6  2  3  

Copy the code

Operation process:

  • The first round: Execute script – print 1 – first setTimeout into macro queue – print 4 – Promise into micro queue – second setTimeout into macro queue – print 7 – macro task end start emptying micro task – Execute promise callback print 5 – Microtask clearing
1  4  7  5  

Copy the code
  • Second round: Take out the second setTimeout macro task to execute – print 6 – the microtask is empty
6  

Copy the code
  • Round 3: Take out the first setTimeout macro task execute – print 2 – PROMISE into microqueue – macro task end start emptying microtask – Perform promise callback print 3 – microtask emptying
2 3  

Copy the code

“One more upgrade.”

console.log(1)



setTimeout((a)= > {

  console.log(2)

  Promise.resolve().then((a)= > {

    console.log(3)

  })

}, 0);



new Promise((resolve, reject) = > {

  console.log(4)

  resolve(5)

}).then(data= > {

  console.log(data)

  console.log(6)

}).then((a)= > {

  console.log(7)



  setTimeout((a)= > {

    console.log(8)

  }, 0);

})



setTimeout((a)= > {

  console.log(9)

}, 0);



console.log(10)

Copy the code
















Is this right?

   1  4  10  5  6  7  2  3  9  8

Copy the code






5 concludes

Analyzing the browser event loop lets us learn the JS running process in the browser and know more thoroughly about the single thread running of JS.

In my own learning of browser event ring, I encountered several unclear points, and I did not find satisfactory answers on the Internet, so I summarized and wrote down my own understanding, if there is any mistake, please leave a message to me, thank you

Two questions

The first one

The event loop flow is a macro task queue that pulls out a macro task – emptying all microtasks – loop, script is not pulled out of the macro task queue, how is a macro task, how is it pushed?

JS is an interpreted language, the compilation process is stored in memory into a syntax tree, and then directly into the execution stack side interpretation side execution, it has not entered the macro task queue

A macro task in my understanding is a task, similar to my example of a release iteration, which mainly does the requirements development of the new version, and also includes clearing all the current bugs (clearing the current micro task), they are all completed before a release iteration (a loop).

The reason script is not in the macro task queue is because the callback after an asynchronous task has finished has to be stored somewhere (the task queue), whereas script is synchronous and does not need to be stored

The second

We all say that micro tasks take precedence over macro tasks, but the explanation is that the micro tasks are executed after the macro tasks are completed. What is the order?

I was also confused when I first started looking at the event loop, isn’t it micro tasks that are higher than macro tasks?

The script macro task is executed first, and then the microtask is executed.

I have also checked a lot of articles that are ambiguous, and THEN I put the application scenarios into the inference, and give a reasonable explanation in my opinion.

The event loop actually runs in a macro – microtask order, and JS runs in that order.

The microtask-macro sequence we’re talking about is asynchronous because script is the first macro task, and it’s synchronous.

All we need to determine is the order of asynchronous execution (scPRit synchronization, of course, is executed first, which is ignored).

So let’s mask script by default and look at the order of asynchronous execution: script-microtask-macrotask-microtask