Single-threaded JavaScript
As we all know, JS is a single threaded language, what is single threaded? You can only do one thing at a time
Why is JS designed this way? The main purpose of JS is to manipulate the DOM and operate with the user. Therefore, if JS has two threads, one thread modifies the content on a node, and the other thread modifies the content on the node, then who will be the subject of JS?
So js single thread is of course for high efficiency and safety
In order to improve the computing capacity of multi-core CPU, HTML5 proposes the Web Worker standard, which allows JS scripts to create multiple threads, but the child threads are completely controlled by the main thread and cannot operate DOM. So this new standard doesn’t change the single threaded nature of JS
2. Synchronous and asynchronous tasks
Single threading of JS means that all tasks need to be queued, and the previous task is finished before the next task is executed. However, the IO device is very slow. When it needs to read data, the CPU will stop and wait for the IO operation. What’s more, no matter how busy the CPU is, other cpus will not help
Therefore, in order to solve the bad experience caused by blocking IO, JS provides that the main thread can completely ignore the IO device, suspend the waiting task, and then continue to run the next task. After the IO device running result comes out, it can come back and continue to execute the pending task. This is an asynchronous operation.
Therefore, all tasks can be divided into two types, one is synchronous task, one is asynchronous task
- Synchronization tasks: Execute tasks on the main thread. The next task can be executed only after the previous task is completed. If the previous task is not completed, the thread will wait until the task is completed before continuing to execute
- Asynchronous task: The task does not enter the main thread, but enters the “message queue”. The main thread does not wait forever, but continues to execute the following tasks. An asynchronous task is executed only when the message queue notifies the main thread that it is ready to execute
Now let’s look at the execution mechanism of asynchronous tasks:
- All synchronization tasks are executed on the main thread, forming an execution stack
- In addition to the main thread, there is one
The message queue
, as long as the asynchronous task has a running result, inThe message queue
To notify the main thread - Once the
Execution stack
After all synchronization tasks are completed, the system reads the dataThe message queue
, the corresponding event ends the waiting state, enters the main thread, and begins execution
The main thread will continue to perform the above three steps, and whenever the main thread is empty, it will read the message queue, which is how JavaScript executes
Message queues and event loops
Message queues are queues and follow the first-in, first-out principle. Each time the IO thread completes a task, it adds that task to the message queue
Therefore, the tasks entered first will be read by the main thread first. As soon as the execution stack is cleared, that is, the synchronization task has been completed, the tasks in the message queue will enter the main thread in turn. But there is a special case, that is the timer, timer time is not added to the main thread
Now that we know that after an asynchronous operation, the message queue notifies the main thread that it is ready to fetch the event, the question is, how does this notification work?
The answer is the event loop
Event Loop: An Event Loop is the process in which the main thread repeatedly fetches and executes messages from the message queue
The event in this case is the familiar callback function that was added when the asynchronous task was registered
So, the worker thread adds events to the message queue, and the main thread reads them through an event loop. In reality, the main thread does only one thing: it reads messages from the task pair column, executes messages, reads and executes messages until the message queue is empty. And each time the main thread will fetch the next message only after the current message has been executed
Here’s a diagram to better illustrate the process:
When the main thread runs, it generates heap and stack. The code in the stack calls external apis, which add various events to the message queue. When the code in the stack finishes executing, the main thread reads the message queue and executes the corresponding callback functions of those events
4. Timer
Let’s look at synchronous callbacks first
function callback() {
console.log('I'm a synchronous callback');
}
function bar(fn) {
console.log(123);
fn();
console.log(456);
}
bar(callback);
/ / 123
// I am a synchronous callback
/ / 456
Copy the code
The callback function is passed as an argument to the bar function, in which case the callback is a callback, and a synchronous callback
Let’s look at an example of an asynchronous callback:
function foo() {
console.log('I'm an asynchronous callback');
}
function bar(fn) {
console.log(123);
setTimeout(fn, 1000);
console.log(456);
}
bar(foo);
/ / 123
/ / 456
// I am an asynchronous callback
Copy the code
The setTimeout is delayed for 1s after the execution of the bar function. The process of executing the callback outside the main function is called an asynchronous callback
Obviously, the setTimeout() timer is an asynchronous task, and the system will execute the synchronization task in the execution stack before going back to execute the event in the message queue
Even if the timer delay time is 0
function foo() {
console.log('I'm an asynchronous callback');
}
function bar(fn) {
console.log(123);
setTimeout(fn, 0);
console.log(456);
}
bar(foo);
/ / 123
/ / 456
// I am an asynchronous callback
Copy the code
Because setTimeout is an asynchronous task in nature, it will be suspended in any case. After js executes the synchronous task first, it finds that the task in the message queue can be executed (setTimeout delay time expires), and then it will execute it
It is worth noting:
- Timer events though are added to
Task queue
Yes, but not until it is timed to complete - If it is already at the top of the queue, but the timing has not yet expired, it will not be executed, and subsequent events will be executed first
In addition, an asynchronous callback is a callback function that executes outside the main function, usually in one of two ways:
- One: add asynchronous tasks to the end of the message queue
- Second: add asynchronous tasks to the microtask queue so that the microtask can be executed at the end of the current task