Nodejs事件循环

事件循环原理
1、node 的初始化* 初始化 node 环境。* 执行输入代码。* 执行 process.nextTick 回调。*
执行 microtasks。
2、进入 event-loop* 进入 timers 阶段* 检查 timer 队列是否有到期的 timer 回调,如果有,
将到期的 timer 回调按照 timerId 升序执行。* 检查是否有 process.nextTick 任务,如果 有,全部执行。* 检查是否有microtask,如果有,全部执行。* 退出该阶段。* 进入IO callbacks阶段。* 检查是否有 pending 的 I/O 回调。如果有,执行回调。如果没有,退出 该阶段。* 检查是否有 process.nextTick 任务,如果有,全部执行。* 检查是否有 microtask,如果有,全部执行。* 退出该阶段。* 进入 idle,prepare 阶段:* 这两个阶段 与我们编程关系不大,暂且按下不表。* 进入 poll 阶段* 首先检查是否存在尚未完成的回 调,如果存在,那么分两种情况。* 第一种情况:* 如果有可用回调(可用回调包含到期的 定时器还有一些IO事件等),执行所有可用回调。* 检查是否有 process.nextTick 回调, 如果有,全部执行。* 检查是否有 microtaks,如果有,全部执行。* 退出该阶段。* 第二 种情况:* 如果没有可用回调。* 检查是否有 immediate 回调,如果有,退出 poll 阶段。 如果没有,阻塞在此阶段,等待新的事件通知。* 如果不存在尚未完成的回调,退出poll阶 段。* 进入 check 阶段。* 如果有immediate回调,则执行所有immediate回调。* 检查是否 有 process.nextTick 回调,如果有,全部执行。* 检查是否有 microtaks,如果有,全部执 行。* 退出 check 阶段* 进入 closing 阶段。* 如果有immediate回调,则执行所有 immediate回调。* 检查是否有 process.nextTick 回调,如果有,全部执行。* 检查是否有 microtaks,如果有,全部执行。* 退出 closing 阶段* 检查是否有活跃的 handles(定时 器、IO等事件句柄)。* 如果有,继续下一轮循环。* 如果没有,结束事件循环,退出程 序。
细心的童鞋可以发现,在事件循环的每一个子阶段退出之前都会按顺序执行如下过程:
检查是否有 process.nextTick 回调,如果有,全部执行。
检查是否有 microtaks,如果有,全部执行。
退出当前阶段。
也就是,在每个 tick 阶段都会执行 微任务队列,先执行 process.nextTick 队列,在执行 Promise 队列
阶段一:timers 定时器
定时器阶段会执行 setTimeout() 和 setInterval() 两个回调函数,在这个阶段主线程会检查当前时间是否满足定时器的条件,如果满足就执行,不满足会跳过进入下一个阶段,如果在下一个阶段阻塞了,那么再进入定时器执行时,时间可能就不那么准确了。
阶段二:pending callbacks
pending callbacks 意为挂起的回调函数,此阶段对某些系统操作(如 TCP 错误类型)执行回调。例如,如果 TCP 套接字在尝试连接时接收到 ECONNREFUSED,则某些 *nix 的系统希望等待报告错误。这将被排队以在 挂起的回调阶段执行。
以下回调函数排除
setTimeout()和setInterval()的回调函数
setImmediate()的回调函数
用于关闭请求的回调函数,比如socket.on('close', ...)
阶段三:idle, prepare
该阶段仅系统内部(libuv)调用
阶段四:poll
检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,setImmediate() 之外),其余情况 node 将在此处阻塞。
阶段五:check
setImmediate() 回调函数在这里执行。
阶段六:close callbacks
一些准备关闭的回调函数,如:socket.on('close', ...)。