summary

We often say the Javascript (JS) below is a single-threaded programming language, which allows only a single thread means that the same time of code executed on the main thread, so need to wait for a long time to perform some task, they will occupy the thread, which can cause subsequent code cannot execute, program can’t normal use. This is the drawback of single thread, and JS uses the Event Loop mechanism to solve this drawback.

HTML5 standard put forward the concept of new technology WebWork, it is used to achieve “multi-threading” technology, but this is actually a single thread simulation, JS single thread this core has not changed, this need to pay attention to oh.

Understanding the event loop mechanism involves a number of confusing concepts, such as THE JS execution mechanism, call stack (execution stack), task queue (message queue), macro and micro tasks. Of course, after knowing these things, you will have a deeper understanding of JS, without saying much, we begin this happy journey.

Stack, heap, queue

Before we get to the topic, let’s take a look at three data structure types, which I believe are familiar to many people. The reason for this is that the following concepts may be involved, and I hope you can get a better understanding of them.

  • Stack (Stack) : A Stack is a special list in which elements can only be accessed through one end of the list, called the top of the Stack. The stack is referred to as a last-in-first-out (LIFO) data structure. Since the stack is last-in, first-out, any element that is not at the top of the stack cannot be accessed. To get to the bottom of the stack, you have to remove the top element.

  • Queue: Stack data structures are accessed using LIFO(last In, First Out) and Queue data structures are accessed using FIFO(Fist-in-first-out). Queues add items to the end of the list and remove items from the front of the list.

  • Heap: A Heap is a sorted tree data structure, with each node having a value. Usually when we talk about the data structure of the heap, we refer to the binary heap. A heap is characterized by the minimum (or maximum) value of the root node, and the two subtrees of the root node are also the same heap. Because of this feature of the heap, it is often used to realize the priority queue. The access of the heap is random, which is just like taking books from the shelf of the library. Although the books are placed in order, we do not need to take out all the books in front of us like the stack when we want to take any book, we only need to care about the name of the book.

In fact, I feel that the whole so many concepts are useless, can not remember (T_T). It’s clear that these three data structures are important, but how do you remember them? I’m memorizing its properties with examples:

  • Stack: First in, last out or last in, first out. It’s like a clip, a bullet that goes into the clip first and then comes out last.
  • Queue: first in first out or last in last out. Like supermarket queue settlement, the first queue of people settlement must go first.
  • Stack: discharge in sequence and take at will. Like a bookshelf of books, we arrange them alphabetically or by category, but when we pick up the book, we know the title and we can find the book directly.

JS execution mechanism

We know that JS is executed line by line from top to bottom, but what if we encounter a long task during execution? JS just have to wait? Not so silly, clever programmer split tasks into synchronous and asynchronous tasks to avoid single-threaded JS blocking during execution. Here is a sketch of the execution process:

Illustration:

  • When JS starts executing, it divides tasks into synchronous and asynchronous tasks.

    Task: A task is any JavaScript code that is scheduled by standard mechanisms such as executing a piece of program from scratch, or executing an event callback, or an interval/timeout being fired. Explain in detail

    Synchronous tasks: Tasks that are arranged and executed on the main thread. You can perform the next task only after the previous task is completed.

    Asynchronous task: a task that does not enter the main thread but enters the “task queue”. An asynchronous task is executed on the main thread only when the task queue notifies the main thread that it has a result and is ready to execute.

  • The synchronization tasks are directly executed on the main thread.

  • The asynchronous task will enter the “task queue” and wait for the result of the asynchronous task. The registered callback function will be put into the task queue and wait. When the main thread is idle (the execution stack is cleared), it will be read to the execution stack and wait for the execution of the main thread.

  • Asynchronous tasks can be subdivided into macro tasks and micro tasks.

Macro and micro tasks

JS subdivides asynchronous tasks into macro tasks and micro tasks. So, what are these two?

  • Macrotask: it can be understood that the code executed by each execution stack is a Macrotask. This includes fetching event callbacks one at a time from the event queue and placing them on the execution stack.
  • Microtask: a task that is executed immediately after the execution of the current macro task. That is, after the current macro task and before the next macro task. Microtasks are created while running macro/synchronous tasks and are specific to the current task.

Why are macro and micro tasks created? As mentioned earlier, asynchronous tasks go into what is called a “task queue”, and the task queue has the nature of a queue. First in, first out, that is, a task added later must wait for the previous task to complete. If important data needs to be retrieved or an event needs to be processed during execution, it cannot be processed in a timely manner according to the first-in, first-out order of the queue. This gives rise to macro tasks and microtasks, which allow asynchronous tasks to be processed in a timely manner.

What’s an example of a macro task versus a micro task? Ever see a case in point is very good, macro and micro tasks for image is: you go to business hall to run a business, there will be a line number when call the number to you when you go to window do prepaid phone business tasks performed (macro), when you deal with top-up and you want to change a package (task), staff will directly help you to do at this time, can’t get you to the end of the line. Example source

What are the macro and micro tasks?

  • Macro task:

    1. script(Overall code)
    2. setTimeout
    3. setInterval
    4. I/O operations
    5. The UI rendering
    6. setImmediate(Node.js environment)
  • Micro tasks:

    1. Promise.then
    2. Mutation Observer API (The specific use)
    3. Process.nextTick(Node only)
    4. Object.observe(waste)

What’s the difference between a microtask and a task?

  1. First, every time a task exists, the event loop checks to see if the task is ceding control to other JavaScript code. If not, the event loop runs all the microtasks in the microtask queue. The microtask loop is then processed multiple times in each iteration of the event loop, including after the event and other callbacks are processed.

  2. Second, if a microtask adds more microtasks to the queue by calling queueMicrotask(), those newly added microtasks will run before the next task. This is because the event loop keeps calling microtasks until there are no remaining microtasks in the queue, even if more microtasks keep being added.

Note: Since the microtasks themselves can be loaded with more microtasks, and the event loop continues to process the microtasks until the queue is empty, there is a real risk that the event loop will process the microtasks indefinitely. Be careful how you handle recursively increasing microtasks.

Event loop

After understanding the whole process of JS execution mechanism, Event Loop is relatively simple and easy to understand. At the beginning, we mentioned that its emergence is to solve the disadvantages brought by JS single thread. It is also the most core part and the most important part of the whole JS single thread execution process.

Before we get to event loops, we need to cover the concept of an execution stack (also known as a call stack). What is it? In Internet terms, all synchronization tasks are executed on the main thread, forming an execution stack. Explain in detail

You can also simply say that this is where JS code is executed on the main thread, such as console.log(1), when executed on the stack, the console prints 1. Of course, from its name we need to know that it has the characteristics of stack advanced after out oh.

Again, let’s start with the picture above:

  1. All synchronization tasks are executed on the main thread, forming an execution stack.
  2. In addition to the main thread, there is a “task queue”, which stores the callback function of the asynchronous task after it has run. That is, an event is placed in the “task queue” when the asynchronous task has run result.
  3. Once all the synchronization tasks in the execution stack are completed, the main thread reads the task queue to see what events are in it. Then push those corresponding asynchronous tasks onto the execution stack and start executing.

The main thread repeats step 3, creating what we call an event loop.

The concept I remember is that the main thread reads asynchronous task execution from the task queue and repeats the process over and over again, which is called an event loop.

Take a chestnut

Speak so much, for example chestnut is the most real, we will analyze it carefully below.

Var p = new Promise((resolve, reject) => {console.log(' promise-initialize '); var p = new Promise((resolve, reject) => {console.log(' promise-initialize '); Resolve (' promise-result ')}) function fn1() {console.log(' promise-result '); } function fn2() {console.log('fn2 - start executing '); SetTimeout (() => {console.log('setTimeout - execute '); }) fn1(); Console. log('fn2 - Do it again '); P.chen (res => {console.log('Promise - first then: '+ res); = > {}). Then ((). The console log (' Promise - the second then '); }) } fn2();Copy the code
  1. First of all, I’m going to do it from top to bottomPromise()Press the object intoExecution stackExecute, output“Promise-initialization”And givepI assign onePromiseObject, and thenExecution stackPromise()Object pops, which isExecution stackCleared.
  2. So if I keep going, regardless of the declarations of the two functions, I go tofn2()The call to thefn2()Push on the stack to execute, output“Fn2 – Commenced”, to continue tosetTimeout()Push it on the stack, it will put it insideConsole. log('setTimeout - execute ');Statements intoTask queueIn the pop-upsetTimeout().Execution stackfn2()Continue calling.
  3. Down, downfn1()The call to thefn1()Push on the stack to execute, output“Fn1 – Execution”That pop upfn1(), down, print it out again“Fn2 – Run again”.
  4. Let’s go down to the first one.then(), push it on the stack to execute, will put it insideConsole. log('Promise - first then: '+ res);Statements intoMicrotask queueIn, pop it up and press the second one.then(), to continue toConsole. log(' promise-second then');Statements intoMicrotask queueIn, pop it.
  5. Here,fn2()It’s done. It’s gonna beExecution stackIt pops, and the stack clears again.
  6. The synchronization task is finished, the main thread is free, start readingMicrotask queueIn accordance with the fifO nature of the queue, the queue will be firstConsole. log('Promise - first then: '+ res);Press the statements intoExecution stackExecute, output“Promise – first then: Promise – result”, and then pop, then press another statement, output“Promise – The second Then”Pop up.
  7. Execution stackIt’s cleared again. It’s readingTask queue,Console. log('setTimeout - execute ');Statements are pushed onto the stack for execution and output“SetTimeout – Execute”And pop up.

This is the entire execution process, the text is a little too much and messy, but if you look carefully, you should be able to see it (-^ ○ ^-), the steps add black text corresponding to the output below.

The above example should be easy to understand, but it doesn’t quite capture the essence of Event Loop, so let’s change it.

. Function fn2() {console.log('fn2 - start executing '); SetTimeout (() => {console.log('setTimeout - execute '); / / start setTimeout () = > {the console. The log (' task and a macro ')}) p.t hen (() = > {the console. The log (' third then Promise - ')})}) / / end fn1 ();  Console. log('fn2 - Do it again '); P.chen (res => {console.log('Promise - first then: '+ res); }). Then (() = > {the console. The log (' Promise - the second then ')})} fn2 ();Copy the code

We added a macro task and a micro task to a macro task, and we went straight to the output:

Does it meet your expectations? Since.then() is a microtask, why is it not executed together with the reading of the microtask queue mentioned above in step 6? The reason is simply that the macro task in the red box below has not been executed. We should look at them as a whole, they haven’t been refined.

The main purpose of this point is to show that the execution of a macro task may continue to generate macro tasks and microtasks, and then the main thread continues to read the microtask queue and task queue to form the Event Loop process.

At this point, this article is finished, sa Hua Sa hua.

I hope this article has been helpful to you. If you have any questions, I am looking forward to your comments. Same old, likes + comments = you know it, favorites = you know it.