事件循环(event loop)

247 阅读2分钟

node环境

nodejs中的事件循环依赖六个阶段,每个阶段对应一种类型的事件,并且绑定一个事件队列。每次事件循环进入每个阶段,都会从对应的队列中取出事件进行执行,直到此次事件循环的事件队列为空或者执行该阶段的事件达到系统最大数量,然后进入下个阶段。

timer: 执行到期的setTimeOut和setInterval回调。

I/O: 执行上次循环中遗留的(在poll阶段推迟执行)i/o回调。

idle, prepare: node内部事件执行。

poll: 这是个观察的阶段,包括文件读取,网络请求等等,如果有对应的回调可以执行,则执行,直到时间耗尽或者达到系统最大数量为止(进入下一阶段)。如果没有对应的回调可以执行,则在一段时间内会持续监听(阻塞)。这个阻塞时间是按照以下规则来确定的:首先,如果此时check阶段,close阶段,I/O阶段有任务可以执行,则设置阻塞时间为零(立即进入下一阶段);否则取最近的定时器执行,如果最近的定时器到期,则立即执行该定时器回调;如果没有定时器任务需要执行,则一直阻塞在poll阶段。

check: 执行setImmediate注册的回调。

close:执行close事件注册的回调。

nextTickQueue和microTaskQueue: 这两种事件在从当前阶段执行完毕,进入下次阶段之前执行,和其他类型的事件不同的是,其他事件都有对应的系统最大数量,而这两种类型的事件没有系统依赖的最大限制,node会一直执行它们的队列,直到队列为空(可能造成死循环)。而且nextTick的优先级大于microTask。

浏览器环境

浏览器环境的事件循环依赖两个事件队列,一个是宏任务的事件队列(由事件触发线程维护),一个是微任务的事件队列(由js引擎线程维护)。

1. 将当前执行上下文压入执行调用栈(js线程),js调用栈执行当前的同步任务。遇到异步任务,宏任务由对应的其他线程接管(事件触发线程,定时器线程,http请求线程),当这些线程处理完这些任务后,将回调添加到事件队列(event queue)队尾;微任务直接添加到微任务的事件队列中。当此次evet loop结束后(执行栈为空),会查看微任务队列中是否有任务等待执行,有的话将任务全部依次执行。执行完微任务后,从event queue队列首部取出一个回调压入执行栈进行执行。依次类推。


参考链接

1. www.zcfy.cc/article/nod…

2. juejin.cn/post/684490…