Appearance
什么是事件循环
宏任务和微任务
宏任务
- script(整体代码)
- setTimeout
- setInterval
- setImmediate
- MessageChannel
- I/O
微任务
- process.nextTick
- Promise
- Async/Await(实际就是 promise)
- MutationObserver (回调函数会进入微任务队列)
浏览器事件循环
总的结论就是,执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。
Node 事件循环
node 的事件循环的阶段顺序为: 输入数据阶段(incoming data)->轮询阶段(poll)->检查阶段(check)->关闭事件回调阶段(close callback)->定时器检测阶段(timers)->I/O 事件回调阶段(I/O callbacks)->闲置阶段(idle, prepare)->轮询阶段...
阶段概述
- 定时器检测阶段(timers):本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数。
- I/O 事件回调阶段(I/O callbacks):执行延迟到下一个循环迭代的 I/O 回调,即上一轮循环中未被执行的一些 I/O 回调。
- 闲置阶段(idle, prepare):仅系统内部使用。
- 轮询阶段(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
- 检查阶段(check):setImmediate() 回调函数在这里执行
- 关闭事件回调阶段(close callback):一些关闭的回调函数,如:socket.on('close', ...)。
poll 阶段
如果当前
已经存在定时器
,而且有定时器到时间了
,拿出来执行,eventLoop 将回到timers 阶段
。如果
没有定时器
, 会去看回调函数队列
。如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者达到系统限制
如果 poll 队列为空时,会有两件事发生
- 如果有
setImmediate
回调需要执行,poll 阶段会停止并且进入到check 阶段
执行回调 - 如果没有
setImmediate
回调需要执行,会等待回调被加入到队列中
并立即执行回调,这里同样会有个超时时间设置防止一直等待下去,一段时间后自动进入 check 阶段
。
- 如果有
check
check 阶段。这是一个比较简单的阶段,直接执行 setImmdiate 的回调。
process.nextTick
process.nextTick 是一个独立于 eventLoop 的任务队列。 在每一个 eventLoop 阶段完成后会去检查 nextTick 队列,如果里面有任务,会让这部分任务优先于微任务执行。
在 node11 之前,因为每一个 eventLoop 阶段完成后会去检查 nextTick 队列,如果里面有任务,会让这部分任务优先于微任务执行
在 node11 之后,process.nextTick 是微任务的一种,是先进入 check 阶段,执行一个 setImmediate 宏任务,然后执行其微任务队列,再执行下一个宏任务及其微任务
node11 之后一些特性已经向浏览器看齐了,总的变化一句话来说就是,如果是 node11 版本一旦执行一个阶段里的一个宏任务(setTimeout,setInterval 和 setImmediate)就立刻执行对应的微任务队列