1. About the javascript
Javascript is a single-threaded language. Web-worker was proposed in the latest HTML5, but the core that javascript is single-threaded remains unchanged. So all javascript version of the “multithreading” are simulated with a single thread, all javascript multithreading is a paper tiger! (No matter what the new framework new syntax sugar to achieve the so-called asynchronous, in fact, are using synchronous method to simulate)
Javascript event loop
Event loop is a method to implement asynchronous JS, as well as the execution mechanism of JS.
First of all, the browser will be the main task in the queue synchronization task each performed entirely, and then to waiting to see which tasks you can perform the task queue, and then put the task in the main task to perform in the queue, finish the task, such as to see who can perform the task to wait, then put the task in the main task queue execution… And so on. This Loop is called an Event Loop. Js is a single thread, and js tasks are executed one by one. If one task takes too long, the next must also wait. So the question is, if we want to browse the news, but the news contains an ultra hd image that loads slowly, should our web page stay stuck until the image is fully displayed? So smart programmers divide tasks into two categories:
- Synchronization task
- Asynchronous tasks
A diagram shows the event cycle
- Synchronous and asynchronous tasks go to different execution “places”, synchronous tasks go to the main thread, asynchronous tasks go to the Event Table and register functions.
- When the specified Event completes, the Event Table moves this function to the Event Queue.
- If the tasks in the main thread are empty after execution, the Event Queue will read the corresponding function and enter the main thread for execution.
- This process is repeated over and over again, known as an Event Loop.
When is the main thread execution stack empty?
The js engine has a monitoring process that continuously checks to see if the main thread stack is empty, and if it is, checks the Event Queue to see if there are any functions waiting to be called.
So you can view it like this:
- The browser line executes a synchronous task, adds an asynchronous task to the queue when it encounters it, and then continues.
- After all synchronization tasks are executed, wait for all executable microtasks to be executed one by one in the task queue.
- After executing the micro task, take the first macro task that reaches the execution condition first and execute it.
- After the execution, wait for the task queue to clear and execute all the microtasks that have reached the execution conditions.
- If the macro task execution generates micro tasks or the micro task execution generates macro tasks, it is added to the waiting task queue, and then the main thread executes all the micro tasks before executing the macro tasks one by one
In all asynchronous tasks, the first to reach the condition is the first to execute, but the first to reach the execution condition also has a priority problem, which depends on whether the task is a macro task or a micro task; The priority of micro task is higher than that of macro task.
3. Operation mechanism
In an event cycle, each operation is called tick. The task processing model of each tick is complex, but the key steps are as follows:
- Perform a macro task (fetch from event queue if not in stack)
- If a microtask is encountered during execution, it is added to the task queue of the microtask
- Execute all microtasks in the current microtask queue immediately after the macro task is executed (in sequence)
- When the macro task completes, the render is checked, and the GUI thread takes over
- After rendering, the JS thread takes over and starts the next macro task (fetched from the event queue)
Macro task:
(Macro)task: The code that executes on each stack is a macro task (including fetching an event callback from the event queue and placing it on the execution stack). To ensure orderly execution of JS internal (Macro task) and DOM tasks, the browser will re-render the page after the execution of one (Macro task) and before the execution of the next (Macro task).
(macro) task – > render – > (macro) task – >…
Micro tasks:
A microtask is a task that is executed immediately after the execution of the current task. That is, after the current task, before the next task, and before rendering. So it responds faster than setTimeout (setTimeout is task) because there is no need to wait for rendering. That is, after a macroTask has been executed, all microTasks created during its execution have been executed (before rendering).
conclusion
Javascript is single-threaded and runs in the browser, but browsers are multi-threaded and multi-process, so when javascript runs, it creates an execution stack and a message queue, two data structures; Only a single thread is equivalent to the kitchen, and a message queue order is an order, in the first macro tasks for the first time, quite a dish for the first order, will produce some seasoning in a dish, which is equal to the task, in a macro task execution can be carried to meet micro tasks into the stack of micro task queue, Met another macro task was added to the message queue, cooking (macro) task execution is completed, will take out this dish need all seasoning (micro) add (execute), the current main thread execution of the judge (browser multi-process service), from the above message queue to get the next task, executed so step by step down…
– | MacroTasks | Microtasks |
---|---|---|
Who initiated | Host (Node, browser) | JS engine |
Specific events | 1) script (synchronized code), (2) setTimeout/setInterval, (3) the UI rendering/UI events, 4. PostMessage, MessageChannel, 5. SetImmediate, I/O (Node. Js) | 1. Promise, 2.MutaionObserver, 3.Object. Process.nexttick (node.js) |
Who first run | After running | First run |
Will a new tick be triggered | will | Don’t |
case
setTimeout(function(){ console.log('1') }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3') }); console.log('4'); / / 2 3 4 1Copy the code
Analysis:
- Settimeout is a macro task that is placed in the eventQueue of the macro task even though it was executed first
- Scroll down to check for microtasks and see that the code inside the Promise callback is synchronized (microtasks) output 2
- The then function puts him in the sequence of microtasks.
- The main code block (macro task) outputs 4
- All code execution of the main process is complete. Get the function back from the microtask queue and print 3 microtasks complete
- Take the function from the queue of the macro task. Output 1
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
Copy the code
- The main code block outputs 1
- Macro task 1, setTimeout (2, 5-tetrafluorobenzoic)
- Micro task 1 process (6)
- The promise executes immediately, the callback is synchronous output 7, then microtask 2(8)
- Macro task 2setTimeout(9,10,11,12)
- Perform microtask 1, output 6
- Execute microtask 2, output 8, complete all microtasks (Yu Hong Task 1, 2)
- Execute macro task 1, output 2, increment microtask process(3),promise immediately execute callback output 4, microtask (5),
- Perform microtask output 3, output 5
- Execute macro task 2, output 9, increment microtask process(10),promise immediately execute callback output 11, microtask (12),
- Perform microtask output 10, output 12
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
Copy the code
- Output “script start”
- SetTimeout macro task
- Execute async1() to print async1 start, execute async2() to print “async2”.
- After async2 is finished, add code after await async2 to microtask queue async1 end’);
- Continue, new Promise, and sync “promise1”. Promise.then, join the microtask queue,
- The output script end
- View output of microtask queue after macro task is completed async1 end “promise2”
- When all the microtasks are completed, check the macro task and print setTimeout
Change the above code:
async function async1() { console.log('async1 start'); let p = await async2(); console.log(p); console.log('async1 end'); } async function async2() { console.log('async2'); return new Promise((resolve, reject) => { resolve(10); }) } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end'); // Run script start async1 start asynC2 promise1 script end for the first macro task // Run promisE2 10 async1 end // Run setTimeout for the next macro taskCopy the code
- The new promise operation is just like when you create a normal function, so this is a macro task, but then is a micro task
- Promise objects that are resolved will start executing after the end of this level of event queue and before the next level of macro task in this round of microtask queue
- Resolved promises are synchronization tasks that are executed at the end of this cycle, always later than this cycle.
- If two of these microtasks are present, the one that comes first is executed first
- In an async function, an await is received and the current function is jumped, the thread is released, and the code following the await is placed on a microtask queue