Node 中的 EventLoop
Node.js 中的 Event Loop
I/O:读写操作、输入输出、请求响应
1. setImmediate()
- 在
Check阶段被执行 - 不会检查函数队列,而是检查 I/O 队列,当所有 I/O 在当前循环结束后会执行
- 只要
poll队列为空,代码被setImmediate(),无论是否有timers达到下限时间,setImmediate()的代码都先执行。
2. setTimeout(fn, 0)
- 在
Timer阶段被执行 - 回调会被安排在
Timer队列中 - The timer phase is the first phase but is called after the I/O phase as well as the Check phase.
2.2 setImmediate() 和 setTimeout(fn, 0) 为什么有时候顺序随机?
在 Node 中执行下面的代码,会出现两种输出结果
1 | setTimeout(() => { |
一次循环首先进入的是 timers 阶段,进入这个阶段后,主线程会检查一下当前时间,是否满足定时器的条件。如果满足就执行回调函数,否则就离开这个阶段。所以如果进入之前一毫秒已经过去了,那么 setTimeout 的回调会首先执行。setTimeout 最小值是 >= 1ms/4ms,0 会被转成 >= 1ms/4ms 根据具体情况确定
而下面这样就只会出现唯一的结果
1 | //index.js |
因为 fs.readFile 的回调是在 poll 阶段执行的,当其回调执行完毕之后,poll 队列为空,而 setTimeout 入了 timers 队列,此时有代码被 setImmediate(),于是时间循环进入 check 阶段执行回调,之后再下一个时间循环再进入 timers 阶段。
整个顺序是:
- 循环开始,没有
timer,没有I/O 回调 - fs.readFile 执行,把回调加入
I/O Callbacks - 进入
poll阶段,等待文件读取 - 第二轮循环
- 有
I/O 回调,进入I/O Callbacks阶段执行 fs.readFile 的回调 - 随后循环进入
check阶段,发现有setImmediate回调,执行 - 之后下一轮循环才又回到
timers阶段,这时才执行setTimeout的回调
3. process.nextTick()
- 当前操作结束后就执行,和 Event Loop 状态无关,几乎是同步的,在本轮循环就安排执行
- 安排在
nextTickQueue - 会阻塞 I/O,阻塞 Promise
1 | var fs = require('fs'); |
4. Promise.resolve().then
- 这个其实没有把回调安排到下一个循环和
process.nextTick类似,在本轮循环就安排了 - 会进入异步任务里面的”微任务”(microtask)队列
同步任务结束后nextTickQueue结束后microTaskQueue
其他:
- Promises do swallow exceptions
- 创建了两个 Promises 出来,并且什么都没做就抛了第一个,Don’t create promises only to throw them away.
- 上述讨论也就在
Node环境下
在浏览器中,基本只剩setTimeout和Promise.resolve().then
更多的需要关注浏览器的EventLoop