当前位置: 首页 > 科技观察

面试官:说说对 Node.js 中的事件循环机制理解-

时间:2023-03-21 00:09:43 科技观察

面试官:谈谈你对Node.js事件循环机制的理解?转载本文请联系JS每日一问公众号.1、什么是浏览器事件循环?我们了解javascript在浏览器中的事件循环机制,它是按照HTML5定义的规范实现的。在NodeJS中,事件循环是基于libuv实现的,它是一个多平台的专注于异步IO的库,如下图最右侧所示:上图中的EVENT_QUEUE好像只有一个队列,但是EventLoop有6个stage,每个stage都有一个对应的先进先出回调队列2.上一节流程中提到事件循环分为六个stage,分别对应如下:timersstage:thisstage执行定时器的回调(setTimeout,setInterval)定时器检测阶段(timers):该阶段执行定时器的回调,即setTimeout和setInterval里面的I/O事件回调阶段(I/Ocallbacks)的回调函数:I/O回调的执行延迟到下一次循环迭代,也就是上一轮循环没有执行的一些I/O回调idlephase(idle,prepare):仅供系统内部使用轮询阶段(poll):检索n新的I/O事件;执行I/O相关的回调(在几乎所有情况下,除了关闭的回调,那些由定时器和setImmediate()调度的回调)在其他情况下,node会在适当的时候阻塞检查阶段(check):setImmediate()回调函数执行关闭事件回调阶段(closecallback):一些关闭的回调函数,如:socket.on('close',...)每个阶段对应一个队列,当事件循环进入某个阶段时,回调会在这个阶段被执行到队列执行完或者执行完最大回调数后,就会进入下一个处理阶段除了上面6个阶段外,还有process.nextTick,不属于任何阶段事件循环的,属于本阶段和下一阶段的过渡,即本阶段执行结束后,进入下一阶段之前,需要执行回调,类似于插队的流程图,如下:在Node中,re还有macrotasks和microtasks,类似于浏览器中的事件循环。Microtasks对应:nexttickqueue:process.nextTickotherqueue:thenofPromiseCallbackandqueueMicrotasks宏任务对应:timerqueue:setTimeout,setIntervalpollqueue:IOeventcheckqueue:setImmediateclosequeue:closeevent它的执行顺序是:nexttickmicrotaskqueueothermicrotaskqueuetimerqueuepollqueuecheckqueueclosequeueasyncfunctionasync1(){console.log('async1start')awaitasync2()console.log('async1end')}asyncfunctionasync2(){console.log('async2')}console.log('scriptstart')setTimeout(function(){console.log('setTimeout0')},0)setTimeout(function(){console.log('setTimeout2')},300)setImmediate(()=>console.log('setImmediate'));process.nextTick(()=>console.log('nextTick1'));async1();process.nextTick(()=>console.log('nextTick2'));newPromise(function(resolve){console.log('promise1')resolve();console.log('promise2')}).then(function(){console.log('promise3')})console.log('scriptend')分析过程:首先找到同步任务并输出当scriptstart遇到第一个setTimeout时,放入回调函数进入定时器队列,遇到第二次setTimeout,300ms后,p把里面的回调函数ut到timer队列中,遇到第一个setImmediate,把回调函数放到check队列里面当遇到第一个nextTick时,把里面的回调函数放到本轮同步任务执行完后async1函数的执行中,输出async1start,执行async2函数,输出async2,async2进入后输出async1endmicrotask,等待下一轮的eventloop遇到第二个,把里面的回调函数放到本轮同步任务执行完后执行,遇到newPromise,执行里面的立即执行函数,输出promise1,promise2然后里面的回调函数进入microtask当队列遇到同步任务时,输出scriptend执行下一轮返回函数,先依次输出nextTick函数,分别是nextTick1,nextTick2,然后执行microtask队列,依次输出async1end、promise3,执行timer队列,依次输出setTimeout0,然后执行checkQueue,依次输出setImmediate300ms,定时器队列中有任务,执行并输出setTimeout2执行结果如下:.log("}setTimeout"0);setImmediate(()=>{console.log("setImmediate");});输出结果如下:情况一:setTimeoutsetImmediate情况二:setImmediatesetTimeout分析流程:外层同步代码一次性执行。异步API塞进相应的stage,遇到setTimeout。虽然触发设置为0毫秒,但实际上会强制改为1ms。时间到了,会插入到times阶段,setImmediate会插入到check阶段。同步代码执行完后,输入event循环首先进入times阶段,检查当前时间是否超过1毫秒,如果超过1毫秒,满足setTimeout条件,执行回调,如果没有超过1毫秒,跳过空阶段,进入检查阶段,并执行setImmediate回调这里的关键是1ms。如果同步代码执行时间长,进入EventLoop时已经过了1毫秒,先执行setTimeout。如果1毫秒还没到,先执行setImmediate。参考https://segmentfault。com/a/1190000012258592https://juejin.cn/post/6844904100195205133https://vue3js.cn/interview/