node 异步I/O的4要素:请求对象,线程池,观察者,事件循环
event-loop 查看官网介绍 https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
Note:
poll
在node.js里,任何异步方法(除timer,close,setImmediate之外)完成时,都会将其callback加到轮询队列poll queue里,并立即执行。
1.计算它应该block和轮询I/O的时间
2.处理轮询队列中的事件,执行定时器的回调。
当event loop进入poll阶段并且此前代码中没有设置定时器时,有以下两种情况:
1.如果轮询队列不为空,则事件循环将遍历并同步执行它们的回调,直到队列为空或者达到系统上限。
2.如果轮询队列为空,则:
如果已经调用过setImmediate,则事件循环将结束poll阶段并继续执行check阶段,来执行这些定时器。
如果没有调用过setImmediate,则事件循环将阻塞在该阶段,等待有定时器回调加入到poll queue后立即执行。
当event loop进入poll阶段,并且代码设置了定时器:
如果poll是空闲状态,事件循环将检查已达到时间阈值的定时器。如果一个或多个定时器准备就绪,事件循环将回到 timers 阶段来执行那些定时器回调。
check
setTimeout/setInterval回调在timers阶段执行;而setImmediate在check阶段执行,总是在poll阶段完成后执行。
执行定时器的顺序将根据调用它们的上下文而有所不同, 原因详见(cnode讨论帖)[https://cnodejs.org/topic/57d68794cb6f605d360105bf]。
在非I/O周期内调用时,受进程性能限制,执行顺序不确定。(毕竟setTimeout的创建要涉及到红黑树等复杂操作)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// demo1: 打印顺序不定
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
// demo2: 打印顺序:immediate, timeout
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
使用setImmediate优于setTimeout的主要优点是setImmediate将始终在任何定时器之前执行(如果在I/O周期内调度),与存在多少定时器无关。
process.nextTick vs setImmediate
process.nextTick 在各个阶段的切换中间执行,可以理解为总是在当前阶段的末尾触发执行
setImmediate 只在check阶段执行,可以理解为在事件循环的后续迭代或’tick’中触发
其实这两个名字应该换一下,一般来讲process.nextTick比setImmediate更快地触发。
process.nextTick中继续调用nextTick很容易导致饿死现象,官方推荐使用setImmediate来实现异步。