What is an event loop
As we all know, JavaScript is single threaded, and Nodejs can implement non-blocking I/O operations because of the Event Loop.
Event Loop mainly has the following stages, one rectangle represents one stage, as shown below:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ > │ timers │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ I/O callbacks │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ idle, Prepare │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ incoming: │ │ │ poll │ < ─ ─ ─ ─ ─ ┤ connections, │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ data, Etc. │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ check │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ├ ──┤ close callbacks ────── our r companyCopy the code
Each phase maintains a first-in, first-out queue structure, and when the event loop enters a phase, certain actions for that phase are performed, followed by the callback functions in the queue. The next phase occurs when the number of callback functions in the queue or the number of callback functions executed reaches a certain maximum.
timers
At the beginning of the event loop, setTimeout and setInterval callbacks are executed.
When the timer expires, the timer callback is queued and executed in sequence.
Suppose you have four timers A, B and C, with time intervals of 10ms, 20ms and 30ms respectively. When entering the timer phase of the event loop, 25 ms passes, the callbacks of timers A and B are executed and the next phase is entered.
I/O callbacks
Perform callbacks except for setTimeout, setInterval, setImmediate, and close Callbacks.
idle, prepare
Do some internal operations.
poll
This is probably the most important stage in the cycle of events.
If the queue is not empty at this stage, the callbacks in the queue are executed sequentially; If the queue is empty, the setImmediate function is also called, and the check phase is entered. If the queue is empty and there is no setImmediate function call, the event loop waits and executes as soon as a callback is added to the queue.
check
The setImmediate callback is executed at this stage.
close callbacks
For example socket. On (‘ close ‘,…). The callback to etc is executed at this stage.
setTimout vs setImmediate
// timeout_vs_immediate_1.js
setTimeout(function timeout() {
console.log('timeout');
}, 0);
setImmediate(function immediate() {
console.log('immediate');
});
Copy the code
According to the previous version, the event loop enters the Timer phase, performing the setTimeout callback, and the setImmediate callback is not executed until the Check phase is reached. So some people think the output of the above code should be:
$ node timeout_vs_immediate_1.js
timeout
immediate
Copy the code
But the result here is actually inconclusive. This often depends on the performance of the process, and the setTimeout interval, although 0, will actually be 1. So when the initiator enters the event loop and the time has not passed 1ms, the queue in the timer phase is empty and no callback is executed. And then the setImmediate function is called, so the setImmediate callback is called when you go to the Check phase. If the timer loop reaches the timer phase, the setTimeout callback is performed, and the Check phase is followed by the setImmediate callback.
So, either of the following outputs can occur.
$ node timeout_vs_immediate_1.js
timeout
immediate
$ node timeout_vs_immediate_1.js
immediate
timeout
Copy the code
Suppose the above code is placed in an I/0 loop, as in
// timeout_vs_immediate_2.js
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout((a)= > {
console.log('timeout');
}, 0);
setImmediate((a)= > {
console.log('immediate');
});
});
Copy the code
Then the result is definite and the output is as follows
$ node timeout_vs_immediate_2.js
immediate
timeout
Copy the code
process.nextTick()
Process.nexttick () is not part of the event loop, but is also an asynchronous API.
After the current operation is complete, if process.nexttick () is called, then the callback in process.nexttick () will be executed. If process.nexttick () is called, then the callback will be executed. The process.nexttick () callback in the callback is then executed. So procee.nexttick () might block the event loop to the next stage.
// process_nexttick_1.js
let i = 0;
function foo() {
i += 1;
if (i > 3) return;
console.log('foo func');
setTimeout((a)= > {
console.log('timeout');
}, 0);
process.nextTick(foo);
}
setTimeout(foo, 5);
Copy the code
As previously stated, the output above is as follows:
$ node process_nexttick_1.js
foo func
foo func
foo func
timeout
timeout
timeout
Copy the code
You may wonder if process.nexttick () is executed after all the queues in the event loop have been emptied, or after a callback in the queue has been executed. What is the output of the following code?
// process_nexttick_2.js
let i = 0;
function foo() {
i += 1;
if (i > 2) return;
console.log('foo func');
setTimeout((a)= > {
console.log('timeout');
}, 0);
process.nextTick(foo);
}
setTimeout(foo, 2);
setTimeout((a)= > {
console.log('another timeout');
}, 2);
Copy the code
Let’s do it and see what happens
/ / the node version: v11.12.0
$ node process_nexttick_2.js
foo func
foo func
another timeout
timeout
timeout
Copy the code
As a result, process.nexttick () is executed after a callback in the queue completes, as shown above.
You can see the result above, with a comment node version: v11.12.0. This means that the node version running this code is 11.12.0. If your Node version is lower than this, such as 7.10.1, you may get different results.
/ / the node version: v7.10.0
$ node process_nexttick_2.js
foo func
another timeout
foo func
timeout
timeout
Copy the code
Different versions behave differently. I think the new version has been updated and adjusted.
process.nextTick() vs Promise
Process.nexttick () corresponds to nextTickQueue and Promise corresponds to microTaskQueue.
Neither of these are part of the event loop, but they are executed after the current action
// process_nexttick_vs_promise.js
let i = 0;
function foo() {
i += 1;
if (i > 2) return;
console.log('foo func');
setTimeout((a)= > {
console.log('timeout');
}, 0);
Promise.resolve().then(foo);
}
setTimeout(foo, 0);
Promise.resolve().then((a)= > {
console.log('promise');
});
process.nextTick((a)= > {
console.log('nexttick');
});
Copy the code
Run the code and the result is as follows:
$ node process_nexttick_vs_promise.js
nexttick
promise
foo func
foo func
timeout
timeout
Copy the code
If you understand the output above, you should be able to understand the event loop in Nodejs.
The resources
- The Node.js Event Loop, Timers, and process.nextTick()
- Node.js event loop workflow & lifecycle in low level
discuss
The original address
Welcome everyone to discuss together, there is a good place please correct.