Call Stack
A formal description of task queues and event loops, and a general idea of how JavaScript works:
When JavaScript is running, the main thread forms a stack, which is basically the interpreter’s mechanism for the final function execution stream. This Stack is commonly referred to as the Call Stack, or Execution Context Stack.
The call stack, as the name implies, has a LIFO (Last in First Out, Last in First Out) structure. The call stack holds all execution contexts during code execution.
- Each time a function is called, the interpreter adds the execution context of that function to the call stack and begins execution;
- A function that is executing in the call stack, and if other functions are called, the new function is added to the call stack and executed immediately;
- After the current function completes execution, the interpreter clears the call stack of its execution context and continues to execute the remaining code in the remaining execution context.
- However, if the allocated call stack space is used up, an error “stack overflow” will be raised.
Call Stack
1, juejin. Cn/post / 696902…
2, blog.csdn.net/ch834301/ar…
1. Why do we need task queues and loop events
1. JavaScript is single-threaded: you can only run one task at a time. Normally, this isn’t a big deal, but now imagine you’re running a 30-second task, such as requesting data, timers, reading files, and so on. In this case, we wait 30 seconds before we can do anything else (by default, JavaScript runs on the main thread of the browser, so the entire user interface stops), and subsequent statements have to wait until the previous statement finishes executing.
It’s 2021, and no one wants to be stuck on a slow, interactive site.
2. Each render process in the browser has a main thread, and the main thread is very busy dealing with DOM, styling, layout, JavaScript tasks, and various input events. To keep these different types of tasks running smoothly in the main thread, you need a system to schedule them, and that’s the message queue and event loop system we’re going to talk about today.
(For those of you who don’t know how the process thread works when the browser renders, wait for my next article to summarize and I will link to it later.)
3. In order to receive and execute new tasks in the running process of the thread, it is necessary to adopt the event loop mechanism.
A common pattern for receiving messages sent by other threads is to use message queues.
Synchronous and asynchronous tasks
Therefore, JavaScript divides all executing tasks into synchronous and asynchronous tasks.
In fact, every task is doing two things: making calls and getting results.
And synchronization task and the main difference is that the asynchronous task synchronization task after the call, will soon be able to get the result, and asynchronous task will not be able to get results immediately, such as request interface, each interface has a certain response time, according to the network, server, and so on factors, such as timers, again it needs to be fixed after time will return the result.
Therefore, the execution mechanism for synchronous and asynchronous tasks is different.
Synchronous task execution, in fact, is the same as the previous case, according to the code order and call order, support to enter the call stack and execute, after the execution of the call stack is removed.
An asynchronous task, on the other hand, first goes to the call stack, then initiates the call, then the interpreter puts its response callback task into a task queue, and then the call stack removes the task. When the main thread is empty, that is, when all the synchronization tasks have finished, the interpreter reads the task queue and in turn adds the completed asynchronous tasks to the call stack and executes them.
An important point here is that asynchronous tasks are not queued directly; callback functions that execute asynchronous functions (tasks) are pushed to the task queue.
Img – blog. Csdnimg. Cn / 20210629235…
Task team
Here is another knowledge point, is about the task of joining the team.
When a task enters a task queue, it actually takes advantage of other threads in the browser. While JavaScript is a single-threaded language, browsers are not single-threaded. Different threads process different events, and when the corresponding event is ready to execute, the corresponding thread will put it into the task queue.
- Js engine threads: used to interpret and execute JS code, user input, network requests, etc.
- GUI rendering thread: draws the user interface, which is mutually exclusive with the JS main thread (because JS can manipulate the DOM, thus affecting the GUI rendering results);
- HTTP asynchronous network request thread: processes the user’s GET, POST and other requests, and pushes the callback function to the task queue after the result is returned.
- Timing trigger thread:
setInterval
,setTimeout
When the waiting time ends, the execution function is pushed to the task queue. - Browser event handler threadWill:
click
,mouse
After the UI interaction event occurs, the callback function to be executed is placed in the event queue.
What are task queues and circular events
1. Message (task) queue
A message queue is a data structure that holds tasks to be executed. It conforms to the “first in, first out” nature of the queue, that is, to add tasks to the end of the queue; To fetch a task, fetch it from the head of the queue.
In the Task Queue, actually also divided into macro Task Queue (Task Queue) ** and ** Microtask Queue (Microtask Queue), corresponding to which is stored in the macro Task and Microtask.
First, both macro and micro tasks are asynchronous tasks.
1. Common macro tasks: SetTimeout setInterval postMessage MessageChannel setImmediate Mediate setTimeout setInterval postMessage MessageChannel setImmediate Promise.then object.observe MutaionObserver Process.nexttick (Node.js environment)
2. Event loop system
An event loop is a system that listens and executes tasks in a message queue
3. How to use task queues and circular events
Event Loop
In fact, the execution of macro and micro task queues is part of the event loop, so it is put together here.
The specific flow of the event cycle is as follows:
- From the macro task queue, according to the queue order, find the first macro task to execute, put into the call stack, start execution;
- After all synchronization tasks under the macro task are executed, that is, after the call stack is emptied, the macro task is pushed out of the macro task queue, and then the microtask queue starts to execute its microtasks in sequence according to the queue joining order until the microtask queue is emptied.
- When the microtask queue empties, an event loop ends.
- It then finds the next macro task to execute from the macro task queue and begins the second event loop until the macro task queue is empty.
Here are a few highlights:
- When we execute it for the first time, the interpreter will complete the code
script
Put into the macro task queue, so the event loop starts with the first macro task; - If new microtasks are added to the microtask queue during the execution of microtasks, they need to be cleared together. The next macro task will not be executed until the microtask queue is empty.
Link: juejin.cn/post/696902… Source: Nuggets
4. Detail macro tasks (e.g.setTimeout())
To coordinate the execution of these tasks methodically on the main thread, the page process introduces message queues and event loops, and the renderer maintains multiple message queues internally, for example (delayed execution queues and regular message queues). The main thread then takes a for loop, continually fetching and executing tasks from these task queues. We call these tasks in the message queue macro tasks.
- When we execute it for the first time, the interpreter will complete the code
script
Put into the macro task queue, so the event loop starts with the first macro task; - If new microtasks are added to the microtask queue during the execution of microtasks, they need to be cleared together. The next macro task will not be executed until the microtask queue is empty.
Reference article:
1, juejin. Cn/post / 696902…
5. Detail micro-tasks (e.g. Promise, MutationObserver)
A microtask is a function that needs to be executed asynchronously, after the completion of the main function but before the completion of the current macro task.
We know that when JavaScript executes a script, V8 creates a global execution context for it, and at the same time that the global execution context is created, V8 also creates a queue of microtasks internally. As the name implies, this microtask queue is used to store microtasks, because in the current macro task execution process, sometimes there are multiple microtasks, this microtask queue is needed to store these microtasks. However, the microtask queue is for internal use in the V8 engine, so you can’t access it directly through JavaScript.
That is, each macro task is associated with a microtask queue. Then, we need to analyze two important time points — the timing of microtask generation and the timing of microtask queue execution. Let’s start by looking at how microtasks come about, okay? In modern browsers, there are two ways to generate microtasks. The first approach is to use MutationObserver to monitor a DOM node and then modify the node through JavaScript, or add or remove partial nodes to the node. When the DOM node changes, it produces a microtask to record the DOM changes. The second approach is to use promises, which also produce microtasks when promise.resolve () or promise.reject () is called.
Ok, now that we have the microtask in the microtask queue, it’s time to look at when the microtask queue is executed. Typically, when the JavaScript in the current macro task is about to complete, just as the JavaScript engine is about to exit the global execution context and empty the call stack, the JavaScript engine checks the microtasks queue in the global execution context and executes the microtasks in the queue in order. The WHATWG refers to the point at which a microtask is performed as a checkpoint. Of course, there are other checkpoints besides exiting the global execution context, but they are not too heavy and will not be covered here. If a new microtask is created during the execution of a microtask, it is added to the microtask queue. The V8 engine executes the tasks in the microtask queue repeatedly until the queue is empty. That is, new microtasks generated during the execution of a microtask are not postponed to the next macro task, but are carried over to the current macro task.
The Demo case:
This schematic diagram is in the execution of a ParseHTML macro task, in the execution process, encountered JavaScript script, then pause the parsing process, into the execution of JavaScript environment. As you can see from the figure, the global context contains a list of microtasks. In subsequent execution of the JavaScript script, two microtasks were created using Promise and removeChild, respectively, and added to the list of microtasks. Then the JavaScript execution finishes and it’s ready to exit the global execution context. At this point, the checkpoint is reached. The JavaScript engine checks the list of microtasks and finds that there are microtasks in the list. After the microtask queue is empty, exit the global execution context.
Note:
Microtasks and macro tasks are bound, and each macro task creates its own microtask queue when it executes. The duration of a microtask affects the duration of the current macro task. For example, during the execution of a macro task, 100 microtasks are generated, and the execution time of each microtask is 10 milliseconds, then the execution time of 100 microtasks is 1000 milliseconds, or it can be said that the 100 microtasks increase the execution time of the macro task by 1000 milliseconds. So when you’re writing code, it’s important to control the duration of microtasks. In a macro task, create a macro task and a microtask for callbacks, and in either case, the microtask precedes the macro task.
Reference article:
1, time.geekbang.org/column/arti…
6、详解async、await
Async encapsulates the return value of a subsequent function (function expression or Lambda) into a Promise object, and await waits for the Promise to complete and returns the result of resolve.
ES7 introduces a new way to add asynchronous behavior to JavaScript and makes promises easier to use! With the introduction of async and await keywords, we can create an implicit async ‘function that returns a promise. But what should we do?
Previously, we saw that we can explicitly create promises using Promise objects, whether by typing New Promise(() => {}), promise.resolve, or promise.reject.
We can now create asynchronous functions that implicitly return an object, rather than explicitly using Promise objects! This means we don’t need to write any Promise objects anymore.
While it’s a nice fact that async functions implicitly return promises, the real power of async functions is seen when you use the await keyword. With the await keyword, we can pause the asynchronous function while we wait for the value after await to return a Resolved promise. If we want to get the resolved promise value, as we did with the then callback, we can assign the value of the await promise as a variable!
Please refer to the five star article below for specific cases.
Five Star reminder must see article:
1, amazing! Promises & Async/Await process!
Mp.weixin.qq.com/s?__biz=MzA…
2, amazing! Visual JS: dynamic graph demo – Event Loop
Blog.csdn.net/ch834301/ar…
- Dev. To/Lydiahallie…
- Written by Lydia Hallie
A simple js function execution flow (simple summary) :
A simple js function execution flow:
Perform the synchronization methods in this function first, after all the synchronization tasks are completed, such as var num=10, console.log(‘timeout’) steps
Resolve (5). Then (res => res2). Then (res => res2).
Finally, the callback function of the macro task in this function is executed. For example: setTimeout(() => {console.log(‘timeout’)},0)
(Premise: different tasks exist under the circumstances, not to execute) —