Node中的EventLoop

Node.js 借助于V8的加持,在性能方面表现优异,在 single-thread 的基本架构下,可以达到NIO的惊人效果,这里必然存在一个超强的任务处理框架,那就是 Event Loop.

Basic Concepts

Event Loop Workflow

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
  1. timers: 处理 setTimeoutsetInterval 函数的回调
  2. pending callbacks: 处理 I/O 相关的回调函数,并在下一次的循环中处理
  3. idle, prepare: 处理系统相关调用
  4. poll: 处理 I/O 相关的回调函数
  5. check: 处理 setImmediate 相关的回调函数
  6. close callbacks: 处理 close 相关的回调函数

MacroTask & MicroTask

Event Loop 中将任务分类成了 MacroTask & MicroTask,并对其执行的顺序做了限定。

setTimeout & setInterval & setImmediate 都属于宏任务;Promise & process.nextTick 都属于微任务。

微任务追加在本轮循环,在同步任务执行完之后,立即执行,宏任务追加在次轮循环。

微任务中 process.nextTick 任务总是优先执行;因为微任务的队列中有2个独立的子队列:nextTickQuene 和 microTaskQuene;其中一个队列清空之后才会处理下一个队列。

Example

setTimeout(() => console.log(1));
Promise.resolve().then(() => console.log(4));
process.nextTick(() => console.log(3));
setImmediate(() => console.log(2));
Promise.resolve().then(() => console.log(4.1));
Promise.resolve().then(() => console.log(4.2));
Promise.resolve().then(() => console.log(4.3));
process.nextTick(() => console.log(3.3));
process.nextTick(() => console.log(3.2));
Promise.resolve().then(() => console.log(4.4));
process.nextTick(() => console.log(3.1));

上面这段代码,会在 node 11 以上的版本稳定输出:

3
3.3
3.2
3.1
4
4.1
4.2
4.3
4.4
1
2

可以对照上述的 Event Loop 的处理逻辑来对照。

Conclusion

EventLoop 的执行在 Node 和 Browser 上有部分行为上的差异,不过可以通过创建一些测试代码进行测试。

Reference

https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick