JavaScript is a single-threaded language
I’m sure most front-end users know that JavaScript is a single-threaded language. But what is single-threaded multithreading (what)? How does a single thread work? And why is JavaScript designed to be single-threaded? Let’s answer these three questions first
What (Single thread)
Let’s start with processes and threads.
- process
We can understand that a program is a process once it starts executing
- thread
A thread is a stream of execution in which a program executes. Each thread has its own stack, pointer, and storage space. Different threads can share the same piece of code.
- Single thread
When the program is executed, it is executed in accordance with the order set at the time of writing, in order from front to back, all the way down. After the first one is finished, the next one will be executed.
How (How a single thread executes)
Take a look at a very simple piece of code
console.log(1)
console.log(2)
cobsole.log(3)
Copy the code
In a single-threaded language, the above code is executed in a preset order from front to back, and console.log(1) and console.log(2) cannot be executed at the same time. Output result:
1;
2;
3;
Copy the code
Why is JS designed to be single threaded?
Single thread execution is to be able to improve efficiency, and as browser scripting languages, it was designed to function is used to operate the dom, user interaction, if js is multi-threaded, there may be a problem, the multiple threads at the same time operating a dom, multiple threads results from phase at the same time can’t decide how to display on the final page.
What is an execution context
When the JS program is running, it will prepare an execution environment for the code to be executed, including variable object, scope chain, this, parameter, etc. These environments are called its execution context. The global context is also subdivided into global context, function context, and local context. As I write here, I always feel that any one word could write an article, and there are indeed many articles on the web dedicated to parsing the context of this. So the front-end knowledge system is really huge, do front-end tired, hair hurt…
What is a stack
The concept of heap and stack is used for storage in JS. Stack memory stores basic data types such as String,number,init, etc., while heap storage refers to data types such as objects, arrays, etc. But the stack holds the address of the reference type, through which the real object in the heap can be retrieved. It is important to note that the stack is not the same concept as the js runtime execution stack.
JS execution stack
Js code needs a sequence when it executes. When we open a browser, the browser parser needs a mechanism to track the execution of functions, so that when multiple functions are called in the execution environment, this mechanism can track which function is executing and which function is called in the body of the executed function.
- Each time a function is called, it is pushed onto the execution stack
- If the calling function A calls another function B, then function B will also be pushed to the execution stack, and function B will be executed as soon as it is called
- When function B is called, the parser clears it off the execution stack and proceeds to execute the rest of the code in function A’s execution environment
The code:
Function a() {console.log('a function enters the stack and starts executing ') b() console.log('b function clears the stack, Function b(){console.log(' function B enters the stack and starts executing ')} a()Copy the code
The above code executes the logic:
-
The execution stack ignores function A and function B and uses them as execution contexts;
-
The code performs the execution of a() function, and a is pushed to the execution stack call;
-
Execute console.log(‘ function A enters execution stack and starts execution ‘)
-
The b() function is encountered, and the b function is pushed to the execution stack call, into the code logic in the B function
-
Console. log(‘ function B enters execution stack and starts execution ‘)
-
After function B is executed, function B is cleared from the execution stack, and function A is returned to the execution stack to continue
-
Console. log(‘b function cleared from execution stack, continue to execute a function’s code ‘)
-
Function A completes execution and is removed from the execution stack
The above is a very simple JS stack call process. You can think of the stack as a badminton barrel. When function A is encountered, a badminton a is placed in the barrel. If function B is called in function A, then b is placed in the barrel as a new ball. Only after b function is cleared from the stack (badminton B is removed from the barrel) can a function continue to execute (badminton A), this is the last in first out logic of JS stack execution.
Task queue
The logic of stack execution is very simple. There are no asynchronous requests. If there are asynchronous requests, how does the JS parser execute? Modify the above code slightly:
Function a(){console.log('a function enters the stack and starts executing ') b() setTimeout(() => {consolelog(' asynchronous task ')},0) console.log('b function is cleared from the stack, Function b(){console.log(' function B enters the stack and starts executing ')} a()Copy the code
In the above code, an asynchronous function setTimeout is added at line 4. How does this code execute? If the setTimeout method’s second parameter is set to a larger value, setTimout(() => {}, 10000), then the program will be blocked in pending. So, in order to handle asynchronous tasks, you have to play a different way.
This is where the browser’s event mechanism comes in.
An asynchronous task is a task that does not go to the main thread, but to a task queue. After the callback is completed, asynchronous tasks queue up to wait for the main thread to be called when it is free before entering the main thread to execute.
How does single-threaded JavaScript generate such an event mechanism? In fact, this has very little to do with JS, mainly depends on the browser. The browser is the JS runtime environment and it is multi-threaded. Include:
- The main thread performs synchronization tasks sequentially and forms an execution stack
- When an asynchronous task is encountered, it is thrown to the timer trigger thread to process it. After the asynchronous task is processed, it will not immediately return to the execution stack of the main thread, but enter the task queue for waiting
- When the execution stack of the main thread is idle, it checks whether there are any tasks waiting in the task queue. If so, all tasks in the queue are removed to the main thread for execution in first-in-first-out order
- Returns to continue the synchronization task
- Repeat the above steps
The browser mechanism is called EventLoop for the browser, and NodeJS has its own version of the EventLoop, but it’s not quite the same, but we’ll see that later.
Macro task, micro task
So, there are so many asynchronous tasks, usually we see setTimeout, promise asynchronous request, setInterval, etc., and how to distinguish between them? Let’s start with some code:
SetTimeout (() => {console.log(' macro task 1')}, 0) console.log(' mainline 1') new Promise((resolve, resolve, Resolve ('promise resolved')}). Then (res => {console.log(res)}) console.log(' mainline 2').Copy the code
Let’s talk about the process, and then we’ll talk about the answer, but let’s talk about the process and let’s talk about what are macro tasks and what are micro tasks
- The main thread is opened, and the execution begins. SetTimeout is encountered first, which is assigned to macro task processing, and the execution continues.
- When console.log(‘ main thread ‘) is encountered, execute it directly and output ‘main thread’, down;
- When you encounter a new Promise, it is a microtask. The Promise is executed directly and the output is: ‘I am not a microtask 1’. Note that the then of a promise is a microtask. As a microtask in the main thread, the microtask assigned to the main thread is waiting to be executed.
- Go down to console.log(‘ main thread 2′) and the output is’ main thread 2′. At this point, the macro task in the main thread is done, and the MicroTask queue is executed. We assigned the Promise’s THEN to the MicroTask queue, so step 5 starts executing the MicroTast queue in the main thread;
- Execute the code in the Promise’s THEN; Output RES result: ‘Promise resolved’. At this point, the microtask queue in the main thread is cleared, and then the macro task queue MocroTask is started;
- Execute the code in setTimeout in the macro task queue and output ‘macro task 1’.
- The macro task queue is cleared and returned to the main thread.
- Repeat the above process.
Based on the steps above, our output is:
Mainline => Mainline 2 => I am not microtask 1 => mainline 2 => Promise Resolved => macro task 1