Javascript is a single threaded, non-blocking language.
Single thread
Javascript is a single-threaded runtime environment with a single call stack and a program that runs one piece of code at a time.
Take this code as an example
function multiply(a, b) {
return a * b;
}
function square(n) {
return multiply(n, n);
}
function printSquare(n) {
var squared = square(n);
console.log(squared);
}
printSquare(4);
Copy the code
When a function is called, it is put on the stack. When a function is left, it is released from the stack. As you can see, this is a lifO concept
blocking
If all the code is executed in the above manner, you run into a problem
In code like the one below, if one of the network requests is particularly time-consuming, then the rest of the program must wait until the request has finished executing, and there is a block
var foo = $.getSync("//foo.com");
var baz = $.getSync("//baz.com");
var qux = $.getSync("//qux.com");
console.log(foo);
console.log(baz);
console.log(qux);
Copy the code
asynchronous
The browser kernel is multi-threaded, with resident threads as follows
- Render engine thread: responsible for rendering the page
JS
Engine thread: ResponsibleJS
Parsing and execution of- Timed trigger thread: Handles timed events, such as
setTimeout
.setInterval
- Event-triggered thread: processing
DOM
Events (Note:EventTarget.dispatchEventIs called synchronously, so the corresponding event is also executed synchronously;document.getElementById("button").click()
是JavaScript
Initialize the created, processed withdispatchEvent
Same. So whenjs
The callbacks are also executed synchronously, as mentioned in the following example.) - asynchronous
http
Request thread: processinghttp
request
The rendering engine and JS engine can not be carried out at the same time, in case of JS operation of DOM during the rendering process, the browser does not know how to deal with it.
Javascript is single-threaded because the browser has only one JS engine thread open at runtime to parse and execute JS.
But the internal browser is not single-threaded, I/O operations, timer timing and event listening (click, keydown…) Events such as these are handed over to other threads in the browser so that execution of the JS code is not blocked
For example:
console.log("hi");
setTimeout(function cb() {
console.log("cb");
}, 1000);
console.log("over");
Copy the code
As shown in the figure, the asynchronous callback similar to setTimeout will be put into the task queue first when the scheduled time is up, and will be called into the stack for execution only after all the tasks in the main thread are completed.
(ps. Task queuing is a first-in, first-out process)
However, there is a problem with this, the setTimeout callback does not actually execute after 1s.
// If in console.log("over"); Add this code
for(var i = 0; i < 100000; i++) {
for(var j = 0; j < 100000; j++) {
// loop}}Copy the code
For example, when cb() is added to the task queue after 1s, the main thread is still executing the for loop. Cb () can only be pushed to the stack after the for loop is finished. This causes cb() to not execute after 1s.
Microtask
Microtasks, they’re not synchronous tasks. However, it is a task that will be executed immediately after all synchronous tasks have been executed and there is no task in the call stack.
The difference between macro tasks (tasks in a task queue) and microtasks
Microtasks take precedence over macro tasks and are executed immediately when the execution queue is empty.
Macro task only one macro task is executed in an event cycle. If there is UI rendering or other events in the next event cycle, it will be executed after all other events have been processed.
But the task, then, the event loop will continue to call micro task till no retained in micro task queue, even in the more micro task continues to be to join the (namely in the process of executing the task and joined the task, so the new micro tasks will be completed in the event period, so using micro task, Be careful if your code goes into loops that handle microtasks indefinitely.)
So if you want to ensure that your callback code executes in a certain order, use a microtask
Common interfaces for microtasks include: Process. NextTick (unique to Node), Promises, MutationObserver
There are interfaces for macro tasks such as setTimeout, setInterval, setImmediate (Node only), requestAnimationFrame (browser only), I/O, and UI Rendering (browser only).
web worker
For specific operations, please refer to ruan Yifeng’s article: Web Worker Tutorial
The role of a Web Worker is to create a multithreaded environment for JavaScript, allowing the main thread to create Worker threads and assign some tasks to the latter. While the main thread is running, the Worker thread is running in the background, and the two do not interfere with each other. Wait until the Worker thread completes the calculation task, and then return the result to the main thread. It’s a parallel process, not asynchronous; Asynchrony will eventually be executed on the main thread in a serial manner. The advantage of parallel processing is that when some computation-intensive or high-latency tasks are carried out by Worker threads, the main thread (usually responsible for UI interaction) will be smooth and not blocked or slowed down.
Once the Worker thread is created successfully, it will always run and not be interrupted by activities on the main thread (such as the user clicking a button or submitting a form). This helps to respond to the main thread at any time. However, this also causes the Worker to be more resource-intensive, should not be overused and should be shut down once used.
The worker thread executes the process
// main.js
var worker = new Worker(task.js')
worker.postMessage("worker");
Copy the code
worker
Threads are created asynchronously
Var worker = new worker (task.js’); The WebCore::JSWorker object (JSBbindings layer) and the corresponding WebCore::Worker object (WebCore module) are constructed in the kernel, and the asynchronous loading process is initiated according to the initialized URL task-js. The main thread code will not block here waiting for the worker thread to load and execute the specified script file, but will immediately continue to execute the following code down.
postMessage
Message interactions are scheduled by the kernel
In main.js, after the woker thread is created, the postMessage method is called to pass the data. Before the worker thread is created, the message sent from main.js will be stored in a temporary message queue. When the worker thread is created asynchronously, the message will be stored in a temporary message queue. The message data in the temporary message queue is copied to the message queue of The WorkerRunLoop corresponding to Woker, and the worker thread begins to process the message. After a round of message back and forth, when the communication continues, the worker thread has been created at this time, so the message will be directly added to the message queue of WorkerRunLoop;
worker
Thread data communication
There are many ways to communicate data between the main thread and the child thread. The content of the communication can be text or object. It is important to note that this communication is a copy relationship, that is, the value is passed, not the address, the child thread to the communication content changes, does not affect the main thread. In fact, the internal mechanism of the browser is to serialize the communication and then send the serialized string to a child thread, which then restores it.
Event loop in the browser
As you can see from the figure above, this is one more UI rendering process than the previous asynchronous process
The rendering process is divided into:
Structure
– buildDOM
The structure of the treeLayout
– Confirm eachDOM
Approximate location of (Layout)Paint
– Draw eachDOM
Specific content (drawing)
The following conclusions can also be drawn
1. If the main process or the asynchronous queue is blocked, the event loop will stay there foreverUI
Rendering. (Also mentioned earlierjs
Threads andUI
Threads are mutually exclusive.)
// The js thread will stay in the while execution and will not render the UI
while(true) {}
Copy the code
2. Usejs
operationDOM
If so, there is no sequencing (unless there is code to compute other styles).
As in the following example, this is a synchronous operation, and the event loop does not go to the UI render until the synchronization task is completed
// The page does not flash
document.getElementById("div").style.display = "block";
document.getElementById("div").style.display = "none";
Copy the code
What if you have to implement CSS style changes in your JS code?
A. The following case will still be executed synchronously, since requesAnimation is also code that was executed before the actual rendering, so the styles will still be merged
/ / error
box.style.transform = 'translateX(1000px)'
requestAnimationFrame((a)= > {
box.style.tranition = 'transform 1s ease'
box.style.transform = 'translateX(500px)'
})
Copy the code
B. If requestAnimationFrame code appears in the process of executing the requestAnimationFrame, then the requestAnimationFrame code will be executed before the next rendering. So the render will be split into 2 frames
/ / can
box.style.transform = 'translateX(1000px)'
requestAnimationFrame((a)= > { // Render the first frame
requestAnimationFrame((a)= > { // The second frame will be rendered
box.style.transition = 'transform 1s ease'
box.style.transform = 'translateX(500px)'})})Copy the code
C. During execution, calculate other styles, which will also cause the page to be rerendered
// The simpler method is to calculate the style during execution
box.style.transform = 'translateX(1000px)'
getComputedStyle(box) // Pseudo-code, just get the current calculation style
box.style.transition = 'transform 1s ease'
box.style.transform = 'translateX(500px)'
Copy the code
3. setTimeout
Similar asynchronous callbacks do not blockUI
Apply colours to a drawing
Because the asynchronous task is only executed once at a time, the next time, the event loop is checked to see if there are other tasks. If the UI rendering is needed during the process, the browser rendering is performed first, and then the asynchronous callback is performed
requestAnimationFrame
与setTimeout
The different
RequestAnimationFrame this is a special asynchronous task. The registered method is not added to the asynchronous queue, but to the render side of the queue. It is executed 3 steps before rendering (edge/Safari 3 steps after rendering). This can lead to some browser compatibility issues.
Now, let’s look at an example,
1 / / method
function callback() {
moveBoxForwardOnePixel(); // Move 1 px to the right
requestAnimationFrame(callback)
}
callback()
2 / / method
function callback() {
moveBoxForwardOnePixel();
setTimeout(callback, 0)
}
callback()
Copy the code
As a result, setTimeout moves to the right faster than requestAnimationFrame.
Because browsers render at a fixed frequency, requestAnimationFrame is executed only before UI rendering, which is equivalent to rendering a frame only once;
In the time between renders, setTimeout may have been executed many times, and the display will move to the right much faster. That’s why the setTimeout image seems to jump
So for animation, it’s best to use requestAnimationFrame
- Understand user click events and
js
的click()
different
Here are two examples
let button = document.querySelector('#button');
button.addEventListener('click'.function CB1() {
console.log('Listener 1');
setTimeout((a)= > console.log('Timeout 1'))
Promise.resolve().then((a)= > console.log('Promise 1'))}); button.addEventListener('click'.function CB1() {
console.log('Listener 2');
setTimeout((a)= > console.log('Timeout 2'))
Promise.resolve().then((a)= > console.log('Promise 2'))});Copy the code
Result of mouse click: Listener 1 => Promise 1 => Listener 2 => Promise 2 => Timeout 1 => Timeout 2
Button.click () Result: Listener 1 => Listener 2 => Promise 1 => Promise 2 => Timeout 1 => Timeout 2
The reason:
Mouse click:
Js set off click() event:
So if we use js click() method to simulate mouse click event in the automated test, it will lead to the phenomenon that is different from the real result
Let’s do another example
const nextClick = new Promise(resolve= > {
link.addEventListener('click', resolve, {once: true});
});
nextClick.then(event= > {
event.preventDefault();
});
Copy the code
The code above, if is the mouse click event, is no problem, but if the click of the simulation is js () event, the < a > links jump there will be a problem
Because when you’re dealing with links, you usually do the following,
- Create a new event object
eventObject
- Call each
click
Event listener callback and pass ineventObject
- check
eventObject
的canceled
Property, if yescancel
, the link will not be opened, or vice versa (when calledevent.preventDefault()
The attribute is marked ascanceled
)
So if it’s a mouse click, the call stack is empty and the callback function (microtask) is executed, no jump; However, if the click() event is triggered, the call stack is not empty and the callback function (microtask) will not be invoked when the above steps are performed.
node event loop
Node.js is also a single-threaded Event Loop, but it operates differently from a browser environment.
The operating mechanism of Node is as follows:
V8
Engine parsingJavaScript
The script.- After parsing the code, call
Node API
. libuv
The library is responsible forNode API
The execution. It assigns different tasks to different threads, forming aEvent Loop
(event loop) to asynchronously return the execution result of the task toV8
The engine.V8
The engine then returns the results to the user.
Summary of stage
As you can see in the figure below, an event cycle is divided into several stages
- Each time the event loop starts, the current time is logged (this is to reduce the number of time related system calls later)
- It then determines if the current loop object is alive (is it waiting for any asynchronous I/O or timer) and if so continues the subsequent event loop, if not,
Node.js
Process exits timers
: The execution time of this phase is upsetTimeout()
和setInterval()
Callback function of,When it is executed bypoll
Phase controlpending callbacks
: Execution is delayed until the next iteration of the loopI/O
The callback.idle
.prepare
Only:node
Internal use.poll
: Retrieve newI/O
Events; To perform withI/O
Related callbacks (exceptclose
, timer andsetImmediate()
All callbacks other than the callback are executed at this stage), where appropriate,node
It’s going to block here.check
: performsetImmediate()
Callback function.close callbacks
: Some closed callback functions, such as:socket.on('close', ...)
.
Detailed instructions
Timers
This is the timer phase, handling the setTimeout() and setInterval() callbacks. After entering this phase, the main thread checks to see if the current time meets the timer conditions. If so, execute the callback function, otherwise leave the phase.
Pending Callbacks
This phase performs a callback for some system operations, such as the TCP error type. For example, some *nix systems want to wait for an error to be reported if ECONNREFUSED is received when a TCP socket attempts to connect. This will be queued for execution during the pending callback phase.
Poll
The event loop enters the polling phase, and if the timer time is not reached, one of two things will happen:
- If the polling queue is not empty, the event loop accesses the callback queue and executes them synchronously until the queue is empty or the maximum number of callbacks limited by the system is reached.
- If the polling queue is empty, two more things happen:
- If you have
setImmediate
The callback needs to be executed,poll
The phase will stop and entercheck
Phase executes the callback - If there is no
setImmediate
If the callback needs to be executed, it waits for the callback to be added to the queue (blocking occurs), and if a new task is added, the callback is executed immediately. Of course, the maximum timeout period is also set for this phase to prevent blocking during this phase.
- If you have
Once the polling queue is empty, the event loop checks to see if there are any timers that have reached the scheduled time, and if one or more timers are ready, the event loop performs a callback to these timers through the check, close, and next polling timer phases
Close callbacks
If the socket or handler is suddenly closed (such as socket.destroy()), the ‘close’ event is emitted at this stage. Otherwise it will be issued through process.nexttick ().
Look at an example
In the following example, the associative file reading process takes 95ms, and the execution of the callback function in the read file takes 10ms.
After entering the first event loop, the timer timing time didn’t arrive, hang the callback function is also no need to perform, then to the polling phase, at this time to read the file is not complete, so the queue is empty, polling phase is in the midst of a wait state, 95 ms after the file after reading, the callback will be added to the polling the queue and executes, when after the completion of the callback, If there are no more callbacks in the queue, it will check the timer that reaches the timing time the fastest, and then loop to the timer phase and execute the callback function of the timer.
So in this case, the timer actually delays the callback for 105ms, not 100ms
const fs = require('fs');
function someAsyncOperation(callback) {
// Assume this takes 95ms to complete
fs.readFile('/path/to/file', callback);
}
const timeoutScheduled = Date.now();
setTimeout((a)= > {
const delay = Date.now() - timeoutScheduled;
console.log(`${delay}ms have passed since I was scheduled`);
}, 100);
// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation((a)= > {
const startCallback = Date.now();
// do something that will take 10ms...
while (Date.now() - startCallback < 10) {
// do nothing}});Copy the code
SetImmediate with setTimeout () ()
These two methods are similar
setImmediate()
When the current polling phase is complete, it will becheck
Phase execution scriptsetTimeout()
Returns when the polling phase is idle and executed after the set time has reachedtimer
Stage to perform
For example, because the second argument of setTimeout defaults to 0. In practice, Node does not do 0 milliseconds, but at least 1 millisecond. According to the official documentation, the value of the second parameter ranges from 1 millisecond to 2,147,483,647 milliseconds. That is, setTimeout(f, 0) is the same as setTimeout(f, 1).
When you actually execute it, after you enter the event loop, it may or may not be 1 millisecond, depending on the state of the system at the time. If it’s less than 1 millisecond, the Timers phase skips and goes to the Check phase, with the setImmediate callback performed first.
// timeout_vs_immediate.js
setTimeout((a)= > {
console.log('timeout');
}, 0);
setImmediate((a)= > {
console.log('immediate');
});
Copy the code
Results:
// It may be
timeout
immediate
// It could be
immediate
timeout
Copy the code
setImmediate()
、 setTimeout()
In which case is the order of execution confirmed?
If you put two functions in one I/O loop, and setImmediate() is called first, because it’s already in the I/O callback, you’re in the polling phase, and the next phase of the polling phase is the check phase, So you have to perform setImmediate() to get to the next timer phase
// timeout_vs_immediate.js
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout((a)= > {
console.log('timeout');
}, 0);
setImmediate((a)= > {
console.log('immediate');
});
});
/** Result: immediate timeout */
Copy the code
The main advantage of using setImmediate() over setTimeout() is that if setImmediate() is scheduled for an I/O cycle, it will perform before any of the timers, regardless of how many exist
Node’s microtask queue (process.nextTick() and Promise)
The four phases mentioned above (setTimeout, I/O callback, setImmediate(), and Close Callback) are all macro tasks of NodeJS. Unlike the browser, which has only one macro task queue, NodeJS has four different macro task queues.
Nodejs microtask queues have the following two main types:
Next Tick Queue
: it is placedprocess.nextTick(callback)
Of the callback taskOther Micro Queue
: Place othermicrotask
, such asPromise
Etc.
A brief explanation of NodeJS Event Loop:
-
Execute global synchronization code
-
To execute a microTask, execute all tasks in the Next Tick Queue and then all tasks in the Other MicroTask Queue
-
Start to execute macroTask macrotask, a total of 6 phases, starting from the first phase of the execution of the corresponding phase macroTask all tasks
-
In Node 10 and below, the microtask is executed after the callback in the macro task queue has finished in each phase
Timers Queue > Step 2 > I/O Queue > Step 2 > Check Queue > Step 2 > Close Callback Queue > Step 2 > Timers Queue……
-
In Node 11 and later, when a macro task is complete, a microtask is executed
For example:
console.log('start');
setTimeout((a)= > {
process.nextTick(function() {
console.log('process 1');
});
console.log('timer 1');
});
setTimeout((a)= > {
process.nextTick(function() {
console.log('process 2');
});
console.log('timer 2');
})
var promise1 = new Promise((resolve, reject) = > {
console.log('promise 1');
resolve();
}).then((a)= > {
console.log('promise then 1');
})
process.nextTick(function() {
console.log('process 3');
});
console.log('end');
/** * Node 10 and later versions * start * promise 1 * end * Process 3 * promise then 1 * timer 1 * timer 2 * Process 1 * process 2 * /
/** * Versions later than Node 11 * start * promise 1 * end * Process 3 * promise then 1 * timer 1 * Process 1 * timer 2 * Process 2 * /
Copy the code
reference
- What the heck is the event loop anyway? | Philip Roberts
- Jake Archibald: In The Loop – JSConf.Asia
- Further Adventures of the Event Loop – Erin Zimmer – JSConf
- The Event Loop is an Event Loop
- Learn the Event Loop from top to bottom
- Node.js event loops, timers and Process.nexttick ()
- Node.js event loop
- Details of the Node timer
- Understanding the js event loop (Node.js)
- What is the difference between browser and Node Event loops?
- [Turn to Javascript series] A deeper understanding of Web workers
- Create a custom event
- In depth: Microtasks and the JavaScript runtime environment
- Use microtasks in JavaScript with queueMicrotask()
- Browser Event Loop
- JavaScript asynchronous mechanism detailed
Feel the above several videos are good, can not visit the words, you can go to B station, have the same