A, an overview of
Event Loop is an execution model that browsers and NodeJS implement based on different technologies. This article focuses on EventLoop in Node.
1. Event Loop of the browser
The browser’s Event Loop is in the HTML 5 specification (reference: html.spec.whatwg.org/multipage/w… Loop model, the specific implementation process of the browser manufacturers.
2. NodeJS的Event Loop
NodeJS Event Loop is based on Libuv (cross-platform asynchronous IO library) implementation. You can refer to the Node documentation and libuv documentation. Libuv already implements Event Loop (see github.com/libuv/libuv…
EventLoop in the browser
See also: EventLoop in the browser
EventLoop in Node
1. The Node classification Task
2. Node Task Queue description
This section is excerpted from the Node website, with additional instructions. Describe each type of Task Queue in detail
- Timer (timer): This phase executes scheduling callbacks that have been setTimeout() and setInterval().(That is, those timer callbacks that should be executed when the time is up.)
- Pending Callbacks: performDelay to the next loopIterative I/O callbacks.(Refer to poll phase note 2 for this stage)
- Idle, prepare: used only in the system.
- Polling (poll): Retrieves new I/O events; Perform I/ O-related callbacks (in almost all cases, except for the closed callback functions, those scheduled by timers and setImmediate()), while node will perform the I/ O-related callbacks in all other casesIn due timeBlock here.(note:1.When appropriate: Asynchronous I/ OS are not returned when there are no other available tasks. The program blocks here, waiting for an I/O callback;2.When the queue is exhausted or the callback limit is reached, the event loop is moved to the next stage of execution, that is, to the pending callbacks_stage_execution.
- Check: setImmediate() The setImmediate() callback is performed here.
- Close callbacks: Some closed callbacks such as socket.on(‘close’,…) .
3. Js code, function call stack, task queue
Use the following code and GIF to see how the JS code, function call stack, and task queue fit together to execute the code.
setTimeout(() => { console.log("timer"); Promise.resolve().then(() => { console.log("promise"); }); }); setTimeout(() => { console.log("timer2"); Promise.resolve().then(() => { console.log("promise2"); }); });Copy the code
4. Code examples
Verify the process described above with the following code
const fs = require('fs'); Console. log('1. Sync code! '); NextTick (() => {// The macro task is executed, and the macro task is executed. Process.nexttick will immediately execute console.log(' 3.process.nexttick! '); }); SetImmediate (() => {// Check phase executes console.log('5. SetImmediate! '); }); Promise.resolve().then(() => {// the macro task will be executed after process.nexttick console.log('4.Promise.resolve then! '); }); SetTimeout (() => {// after 1s this callback will be pushed to the task queue corresponding to the timer phase console.log(' 7.settimeout '); process.nextTick(() => { console.log('8.setTimeout process.nextTick! '); }); Promise.resolve().then(() => { console.log('9.setTimeout Promise.resolve then'); }); }, 1000); Fs.readfile ('./event.js', () => {// THE I/O callback is executed in the poll phase, the exact time since the poll phase fs.readfile asynchronously reads the file console.log(' 6.fs.readfile '); }); Console. log('2. Sync code! '); // 1. Sync code! // 2. Sync code! // 3.process.nextTick! // 4.Promise.resolve then! // 5.setImmediate! // 6.fs.readFile // 7.setTimeout // 8.setTimeout process.nextTick! // 9.setTimeout Promise.resolve thenCopy the code
5. Flow chart analysis
The following flowchart highlights several key points.
1. How does the poll phase work
2. What is the I/O of pending Callbacks
3. Different processing conditions after the poll stage is completed
4. The poll phase introduction says block here at the right time. What is the right time
6 event loop source
Here is the core code for the Node event loop (taken from the network)
// https://github.com/libuv/libuv/blob/v1.x/src/unix/core.c int uv_run(uv_loop_t * loop, uv_run_mode mode) { int timeout; int r; int ran_pending; // From uv__loop_alive we know that the event loop continues under one of the following conditions: // 1, active handles // 2, active request // 3, Loop closing_handles r = uv__loop_alive(loop); if (! r) uv__update_time(loop); while (r ! = 0 && loop -> stop_flag == 0) {// Update the time variable used in uv__run_timers with uv__update_time(loop); // Timers uv__run_timers(loop); Ran_pending = uv__run_pending(loop); ran_pending = uv__run_pending(loop); // Idle phase uv__run_idle(loop); // uv__run_prepare(loop); // Set the timeout for the poll phase. The timeout is set to 0 in the following cases, which means that the poll phase is not blocked. We will discuss this in more detail in the next poll phase. Idle, I/O callback, and the Handle queue in the close phase are not empty // Otherwise, timeout = 0 in the callback queue in the timer phase. if ((mode == UV_RUN_ONCE && ! ran_pending) || mode == UV_RUN_DEFAULT) { timeout = uv_backend_timeout(loop); } // poll phase uv__io_poll(loop, timeout); // check phase uv__run_check(loop); // Close phase uv__run_closing_handles(loop); // If mode == UV_RUN_ONCE (meaning the process continues), timers are checked once after all phases, If (mode == UV_RUN_ONCE) {uv__update_time(loop); if (mode == UV_RUN_ONCE) {uv__update_time(loop); uv__run_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) { break; } } if (loop -> stop_flag ! = 0) { loop -> stop_flag = 0; } return r; }Copy the code
Four, a few questions
1. SetTimeout && setImmediate Perform the sequence
setTimeout(() => { console.log('1.setTimeout'); }, 0); setImmediate(() => { console.log('2.setImmediate'); });Copy the code
There are two cases
1. SetTimeout () and setImmediate() do not necessarily mediate() when both mediate() are written in main. This time is affected by thread performance. SetTimeout 0 will be executed when set to 1ms in node. The preparation of the time loop takes time. If the preparation time of the event loop is greater than 1ms, setTimeout 0 will be executed first. SetImmediate instead does the trick.
2. SetImmediate () Does not mediate() when setTimeout() and setImmediate() are both written in an I/O callback or the callback of a poll macro task.
2. process.nextTick&&setImmediate
SetImmediate setImmediate is a macro task that triggers execution during the check phase.
Process. nextTick can be understood as a micro-task. We know that micro-task is executed in the interval between macro task execution, so when one (kind) task queue is completed, micro-task will be executed. Process. nextTick will be executed first.
3. The following code execution sequence
setTimeout(() => { console.log('1.setTimeout'); Promise.resolve().then(() => { console.log('1.Promise.resolve'); })}); setTimeout(() => { console.log('2.setTimeout'); Promise.resolve().then(() => { console.log('2.Promise.resolve'); })}); Resolve // 2.Promise. Resolve // ==> Result 2 // 1.setTimeout // 1.Promise. Resolve // 2.setTimeout // 2.Promise.Copy the code
4. Whether the preset time of setTimeout is accurate
Node does not guarantee that timers will run at the preset time because Node’s expiration check on timers may not be accurate, it may be affected by other programs running on the machine, or the main thread may not be idle at that time
const fs = require('fs'); function someAsyncOperation(callback) { fs.readFile('/path/to/file', callback); } const timeoutScheduled = date.now (); setTimeout(() => { const delay = Date.now() - timeoutScheduled; console.log(`${delay}ms have passed since I was scheduled`); }, 100); // after 100ms, the main thread is executing the fs.readFile callback; // do someAsyncOperation which takes 95 ms to complete someAsyncOperation(() => {const startCallback = Date.now(); while (Date.now() - startCallback < 10) { // do nothing } });Copy the code
Reference article:
Nodejs.org/zh-cn/docs/…