Event Loop Overview of the Event Loop
JavaScript is single threaded, because single thread will cause I/O blocking, for example, no response when sending a request may cause page stagnation, to solve this problem, browsers began to support asynchronous JS, asynchronous JS is to put some asynchronous tasks (Ajax, timer) into the task queue, The asynchronous code in the task queue is then read and triggered through an Event Loop, called an Event Loop.
An Event is an Event, such as a file read or timer. Loop means loop, that is, polling is used to execute events. To execute events, there must be sequence. The whole Event loop is to manage the execution sequence of events.
The core code of Event Loop is written in c++ (it belongs to NodeJs category). In essence, Event Loop is constantly reading and executing events in polling mode. Today we will discuss the details of Event Loop.
phase
The Event Loop is divided into the following stages
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ > │ timers │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ I/O callbacks │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ idle, Prepare │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ incoming: │ │ │ poll │ < ─ ─ ─ ─ ─ ┤ connections, │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ data, Etc. │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ check │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ├ ──┤ close callbacks ────── our r companyCopy the code
Each of the above phases has a queue (fifO) containing the callback function. Whenever an Event Loop reaches a stage, it will generally execute some function in the queue (or not)
An overview of the stages
- Timers phase: This phase executes the callback functions of setTimeout and setInterval.
- I/O Callbacks phase: Callbacks that are not executed in the Timers, Close Callbacks, and Check phases are responsible for this phase, which contains almost all callback functions.
- Idle, prepare stage: The stage used internally by the event loop (we don’t care about this stage)
- Poll phase: Obtains new I/O events. Node.js blocks at this stage in some scenarios.
- Check phase: Execute the setImmediate() callback function.
- Close Callbacks phase: Executes the callback function that closes the event, such as fn in socket.on(‘close’, fn).
When a Node.js program terminates, Node.js checks to see if the Event loop is waiting for an asynchronous I/O operation to end and a timer to fire. If not, it closes the Event loop.
timers
This stage is most likely the first stage of the Event Loop, mainly storing macro tasks such as setTimeout or setInterval
poll
This phase is used to fetch new I/O events. When the Event Loop enters the poll phase and finds that the poll queue is empty, the Event Loop checks the latest timer with about 100 milliseconds left. Therefore, the Event Loop decides to stop in the poll phase during this period. When the timer task is about to start, the Event Loop bypasses the poll phase and enters the Check phase
check
NodeJS setImmediate Mediate An API that is often used during interviews, setImmediate, is also a macro task, but features faster execution than timers
SetImmediate and setTimeout
SetImmediate is similar to setTimeout, but the timing of its callback function is different.
SetImmediate Is the Check phase, while setTimeout is timers. What does setImmediate do first?
Let’s take a look at some code
setTimeout(()=>{console.log('timeout')},0)
setImmediate(()=>{console.log('immediate')},0)
Copy the code
In general, it takes precedencesetTmmediate
But the code above is actually executed in this orderIf the value of setTimeout is set to 1000ms, the immediate check phase exists. If the value of setTimeout is set to 1000ms, the immediate check phase must be executed immediately. The Event Loop is in the poll phase, and after all, it takes 1000ms to execute functions in the Timers queue, so the Loop plans to take a break.
Then, as if time were running out, the Loop found the check immediate function and ran to it. After executing it, it ran to timers.
The biggest reason for the confusion above is that the timer is set to 0, depending on the stage at which the Event Loop starts.
If the Event Loop is in the Timers phase and no timer task exists in the queue or the timer task is not running, the Event Loop skips this phase and executes the immediate task first.
If there is a task and the time is up, setTimeout must be executed first, which is why the above code is confusing.
So let’s rewrite it
setTimeout(() = > {
setTimeout(() = > {
console.log("timeout");
}, 0);
setImmediate(() = > {
console.log("immediate");
});
}, 1000);
Copy the code
The above code, after 1 second, executes the arrow function. At this time, the Event Loop is not in the Timers stage. Since the order is immutable, it is always executed firstimmediate
process.nextTick()
You may notice that process.nexttick (), the important asynchronous API, does not appear at any stage because process.Nexttick () is not technically part of the Event loop. In fact, regardless of which stage the Event Loop is currently in, the nextTick queue is executed after the current stage.
setTimeout(() = > {
setTimeout(() = > {
console.log("timeout");
}, 0);
setImmediate(() = > {
console.log("immediate");
});
process.nextTick(() = >{
console.log('nexTick')})},1000);
Copy the code
The above code is executed in this order
NextTick is executed immediately in the current phase. Since the Loop is in the poll phase after the above code is executed, nextTick will be executed first
Let’s change the code again just to make it a little bit more experimental
setTimeout(() = > {
setTimeout(() = > {
console.log("timeout");
process.nextTick(() = > {
console.log("nexTick2");
});
}, 0);
setImmediate(() = > {
console.log("immediate");
});
process.nextTick(() = > {
console.log("nexTick");
});
}, 1000);
Copy the code
Here is the result, and you can see that nextTick is executed immediately in the current phase
nexTick
immediate
timeout
nexTick2
Copy the code
Process. NextTick () and setImmediate ()
The two functions are similar in function and have confusing names.
The callback to process.nexttick () is executed “immediately” during the current Event loop phase. The setImmediate() callback is executed in subsequent Event Loop cycles (tick).
The names should be interchangeable. Process.nexttick () is more immediate than setImmediate().
This is a relic of history and is unlikely to change for backward compatibility. So while the names may sound confusing, they’re unlikely to change in the future.
We recommend that developers use setImmediate() in all situations, as it is more compatible and easier to understand.
Macro and micro tasks
Asynchronous tasks are divided into macro tasks and micro tasks, and micro tasks are always executed before macro tasks
Common macro tasks
Common microtasks
Classic Interview questions
setTimeout(() = > console.log(4))/ / macro task
new Promise(resolve= > {
resolve()// Synchronize tasks
console.log(1) // Synchronize tasks
}).then(() = > {
console.log(3) Micro / / task
})
console.log(2) // Synchronize tasks
Copy the code
Converted to await
setTimeout(_= > console.log(4)) / / macro task
async function main() {
console.log(1) // Synchronize tasks
await Promise.resolve() // Synchronize task equivalent to resolve()
console.log(3) // equivalent to promise.then // microtasks
}
main()
console.log(2) // Synchronize tasks
Copy the code