EventLoop in the browser
- The principle of
- Look at the code and tell the result
Js is a single-threaded language, so all tasks can only be queued to be done one by one, which is obviously inefficient. So event loop is proposed to solve this problem.
In the main program, there are two threads, one that runs the program itself, called the main thread, and the other that communicates with other threads (mainly I/O operations), called the Event loop thread
When an I/O operation occurs, the main thread tells the Event Loop thread to inform the corresponding I/O module to execute it. The main thread then executes the following code. When the I/O is over, the Event Loop thread passes the result to the main thread, which then executes the corresponding callback. The whole mission is over.
Macro task micro task
To keep the tasks running smoothly on the main thread, V8 stores and executes the tasks in queues. There are two types of task queues, including the above task queue and a delay queue. It specializes in timer callbacks such as setTimeout/setInterval. The tasks in both types of task queues are macro tasks
Microtasks are usually scheduled for things that should happen after the current script finishes executing, such as reacting to a series of actions, or making something asynchronous without the cost of a macro task
There are two schemes for the execution of micro-tasks: one is to execute the micro-tasks successively after all the macro tasks are completed; the other is to check the micro-task queue after the execution of a macro task, and execute the micro-tasks successively if they are not empty, and then execute the macro task.
The latter is obviously more satisfying than the latter, otherwise the application will get stuck if the callback is not executed.
nextTick
Process. nextTick is a task queue independent of eventLoop.
The queue is checked at the end of each eventLoop phase, and if there are tasks in it, those tasks take precedence over microtasks.
What are the macro tasks and micro tasks
Common macro tasks are: setTimeout setTimeInterval Common micro tasks are: “MutationObserver, Promise.then(or.reject), and other technologies developed based on Promise (such as the FETCH API), including V8’s garbage collection process.”
Figure source link
Exercise 1:
console.log('start');
setTimeout(() = > {
console.log('timeout');
});
Promise.resolve().then(() = > {
console.log('resolve');
});
console.log('end');
//start
//end
//resolve
//timeout
Copy the code
- First, the entire script is executed as a macro task and is executed directly when it encounters synchronized code
- Print the start
- Put setTimeout into the macro task queue
- Put promise.resolve into the microtask queue
- Print the end
- Perform all microtasks and print resolve
- Execute the macro task and print timeout
Ex 2
setTimeout( () = > console.log(4))
new Promise(resolve= > {
resolve()
console.log(1)
}).then(_= > {
console.log(3)})console.log(2)
/ / 1 2 3 4
Copy the code
That is, the code executed during the instantiation of a New Promise is synchronized, while the callbacks registered in then are executed asynchronously.
The asynchronous task is checked for completion and the corresponding callback is executed after the synchronous code is completed, while the microtask is executed before the macro task
Eventloop in Node.js
Significance of each stage:
- timerperform
setTimeout\setInterval
The callback - I/O callbacks perform all callbacks except close Timer setImmediate
- Idle,prepare: indicates the idle time for internal use
- Poll polls for new I/O events, where Node blocks when appropriate
- Check: Executes the callbacks set by setImmediate().
- Close callbacks: execute such as socket.on(‘close’,…) The callback.
process.nextTick
NextTick has its own queue, independent of eventLoop, which is checked and emptied at the end of each phase.
The main differences between NodeJS and browsers regarding eventLoop
The main difference between the two is that microtasks in the browser are performed in each corresponding macro task, whereas microtasks in Node.js are performed in different stages.
Example:
setTimeout(() = >{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')})},0)
setTimeout(() = >{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')})},0)
Copy the code
Browser execution order
timer1–>promise1—>timer2–>promise2
Nodejs execution order
timer1–>timer2—>promise1—>promise2
-
The global script (main()) is executed, and the two timers are put into the timer queue one by one. After main() is executed, the call stack is idle, and the task queue starts to execute.
-
First enter the timers stage, execute the callback function of Timer1, print timer1, and place the promise1. Then callback into the MicroTask queue. Repeat the same steps to run timer2 and print Timer2.
-
Before the Event loop enters the next phase, all tasks in the MicroTask queue are executed and promisE1 and promisE2 are printed successively
Words:
Browser eventloop:
Js is a single-threaded language, according to the order, but if you encounter an asynchronous task execution time for a long time, the back of the code will be blocked, so the queue is used to store these tasks, wait until they return results, js, return the main thread processing, these tasks in the queue is macro task, and then each macro task corresponds to a task queue, The microtask is designed to solve the asynchronous callback problem. After each macro task is executed, the microtask queue is checked. If there are any microtasks, they are all taken out and executed.
The node eventloop:
The timer performs the setTimeout setInterval callback. The callback phase does not include timer, close, and setImmediate. Then a certain amount of idle time is used internally. Poll then checks for new I/O events and, if setImmediate is useful, jumps to the check phase and executes its callback, followed by the close related callback. Process.nexttick () is called between each phase switch
Third, JS garbage collection mechanism
Before we look at garbage collection, it’s important to understand how data is stored in JS. Js data is divided into basic types and reference types, among which the former uses the stack data structure to store, while the latter uses the heap data structure to store, so the different storage forms lead to their different recycling mechanisms.
Stack recovery
First, the system stack is used to store variables, but it also features the ability to switch execution contexts by moving the top pointer, as shown in the following code
function f(a) {
console.log(a);
}
function func(a) {
f(a);
}
func(1);
Copy the code
- After the func function is called, func is pushed onto the stack. Esp is the top pointer to func
- Then inside, execute f(a), then push F on the stack,esp moves up
- After f(a) is executed, the pointer moves down and f is reclaimed
- After func is executed, it moves down again and the func is reclaimed
As we can see, the collection mechanism of the stack is relatively simple, that is, when the execution context is switched, the top content of the stack is automatically reclaimed, which is the garbage collection mechanism of the stack
Pile of recycling
Reference data is stored in the heap, and you might wonder why not all on the stack, because reference data is relatively complex and the overhead of context switching becomes huge, so use the heap, but at the same time the garbage collection overhead of the heap is also high.
In the heap, we divide the storage space into two parts, the old generation is some permanent memory with a long lifetime, and the old generation is temporary memory with a short lifetime, and the new generation is much smaller than the old generation.
New generation memory reclamation
The new generation memory is also divided into two parts, which we call from and to
From indicates the memory currently in use, and to indicates free memory.
During garbage collection, memory in from is traversed, and surviving variables are moved to to. Non-surviving variables are collected directly.
Why not just recycle in from? Because recycling directly, it will form as a discrete space, has studied the operating system’s friends all know that it is also very easy to understand (of course had not learned), each variable is assigned to can hold its capacity of a storage space in a row, sometimes while remaining capacity enough to hold, but are distributed, that also can’t use, So this amount of discontinuous space is very bad for memory utilization.
So we move to the TO, get structured, align, and swap the roles, so to is from, and from becomes to, and so on and so on. The algorithm is scinsane.
Sometimes, after going through several rounds, we find that there are always several “deadbeat” in memory, so that these “deadbeat” will be promoted to the old generation of memory members.
Old generation memory reclamation
Conditions for the promotion of the new generation of memory:
- Experience the Scavenge
- To Occupy more than 25% of the memory
The old generation of memory space much larger than the new generation, so can’t continue to use the above algorithm processing, v8 methods is the notation, as its name implies is to traverse a circle to mark on each variable, then found variable still alive will mark it canceled, marked the rest of the objects directly can be recycled.
Then there was the inevitable memory fragmentation, and V8’s handling was straightforward, moving straight to one end.
This amount of movement is also the most time-consuming part of recycling.
Incremental tag
Js is a single-threaded language, when the garbage collector will block the main thread, so v8 has taken some measures to adopt the method of incremental tag, in fact, namely in the mark phase: mark a break will then make the main thread, mark phase is over, into the cycle are fragments of memory.