事件循环 Event loop

JS由于是单线程的,为了避免等待I/O的处理阻塞后续事务的执行,具体的运行时往往对I/O操作通过异步操作的形式实现,例如发出请求,读取文件等等。

虽然也会有提供同步执行的形式,但是会阻塞其他事务的执行。

对于这些异步操作,就需要一个事件循环的模型来处理。类似处理完一个事件,然后等待下一个事件,一直这样循环下去。

1
2
3
while(wait_something){
do_something
}

浏览器和Nodejs是现在JS十分常见的两个运行环境,它们的事件循环各自有各自的定义。

浏览器中的事件循环

浏览器中的事件循环需要一个任务队列(task queue)和一个微任务队列(microtask queue)

每次循环先从任务队列读取一个任务并执行,然后循环处理微任务队列的任务直到微任务队列为空,然后执行浏览器渲染相关的操作(所以如果任务耗时太多就会导致浏览器界面卡顿)。

具体的事件循环如图:

详细流程可以参考HTML Event Loops 标准

操作进入的队列

异步操作 放入队列
setTimeout 任务队列
setInterval 任务队列
事件回调 任务队列
Promise.then/catch/finally 微任务队列

Nodejs中的事件循环

Nodejs中的事件循环与浏览器中的事件循环不同,将事件循环主要分为了6个阶段:timers、pending callbacks、idle prepare、poll、check和close callbacks

每个阶段都是一个FIFO的队列

timers阶段处理setTimeoutsetInterval的计时结束的任务,这些任务是以结束时间构建了一个最小堆,每次检测判断堆顶的任务是否满足要求。

pending callbacks阶段处理执行延迟到下一个循环迭代的I/O回调

idle, prepare阶段处理了nodejs自己的一些任务

poll(轮询)阶段检索新的I/O回调,执行它的处理

check阶段处理setImmediate的回调,但是在这之前会再次判断timers阶段是否有完成的任务

close callbacks阶段处理close事件的回调(poll阶段不会处理)

此外每个回调事件处理完后会处理process.nextTick的队列,每处理一个nextTick回调,会清空微任务队列。

详细可以参考 Node.js 事件循环 Nodejs 源码事件循环 Nodejs 任务处理

参考资料

HTML标准 Event Loops

Node.js 事件循环

Nodejs 源码事件循环

Nodejs 任务处理

END

2020-09-24 立项

2020-09-23 立项