node.js的event loop
event loop 的六个阶段:
- timers:执行
setTimeout()
和setInterval()
中到期的callback。 - I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行(i/o callbacks并不是处理文件i/o的callback 而是处理一些系统调用错误,比如网络 stream, pipe, tcp, udp通信的错误callback)
- idle, prepare:仅内部使用
- poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
- check:执行setImmediate的callback
- close callbacks:执行close事件的callback,例如
socket.on("close",func)
process.nextTick在这六个阶段结束时都会存在执行,进入 event loop 前会清空一次 next tick queue 的
event loop的每一次循环都需要依次经过上述的阶段。 每个阶段都有自己的callback队列,每当进入某个阶段,都会从所属的队列中取出callback来执行,当队列为空或者被执行callback的数量达到系统的最大数量时,进入下一阶段。这六个阶段都执行完毕称为一轮循环。
poll阶段的任务就是阻塞等待监听的事件来临,然后执行对应的callback,其中阻塞是带有超时时间的,以下几种情况都会使得超时时间为0
- uv_run处于UV_RUN_NOWAIT模式下
-
uv_stop()
被调用 - 没有活跃的handles和request
- 有活跃的idle handles
- 有等待关闭的handles
如果上述都不符合,则超时时间为距离现在最近的timer;如果没有timer则poll阶段会一直阻塞下去
process.nextTick 内执行 process.nextTick 仍然将 tick 函数注册到当前 microtask 的尾部,所以导致 microtask 永远执行不完; setImmediate 内执行 setImmediate 会将 immediate 函数注册到下一次 event loop 的 check 阶段,而不是当前正在执行的 check 阶段.
poll阶段
poll阶段是衔接整个event loop各个阶段比较重要的阶段:
在node.js里,任何异步方法(除timer,close,setImmediate之外)完成时,都会将其callback加到poll queue里,并立即执行。
poll 阶段有两个主要的功能:
- 处理poll队列(poll quenue)的事件(callback);
- 执行timers的callback,当到达timers指定的时间时;
如果event loop进入了 poll阶段,且代码未设定timer,将会发生下面情况:
- 如果poll queue不为空,event loop将同步的执行queue里的callback,直至queue为空,或执行的callback到达系统上限;
- 如果poll queue为空,将会发生下面情况:
- 如果代码已经被setImmediate()设定了callback, event loop将结束poll阶段进入check阶段,并执行check阶段的queue (check阶段的queue是 setImmediate设定的)
- 如果代码没有设定setImmediate(callback),event loop将阻塞在该阶段等待callbacks加入poll queue;
如果event loop进入了 poll阶段,且代码设定了timer:
- 如果poll queue进入空状态时(即poll 阶段为空闲状态),event loop将检查timers,如果有1个或多个timers时间时间已经到达,event loop将按循环顺序进入 timers 阶段,并执行timer queue.
参考链接:https://cnodejs.org/topic/57d68794cb6f605d360105bf
https://cnodejs.org/topic/5a9108d78d6e16e56bb80882#5a98d9a2ce1c90bc44c445af
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/