登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
AI 队友
登录
注册
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
3
Star
47
Fork
21
DreamCoders
/
CoderGuide
代码
Issues
1169
Pull Requests
0
Wiki
统计
流水线
服务
JavaDoc
PHPDoc
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
说说对Nodejs中的事件循环机制理解?
待办的
#IAG9KX
陌生人
拥有者
创建于
2024-07-29 16:04
<h2>一、是什么</h2><p>在<a href="https://github.com/febobo/web-interview/issues/73" target="">浏览器事件循环</a>中,我们了解到<code>javascript</code>在浏览器中的事件循环机制,其是根据<code>HTML5</code>定义的规范来实现</p><p>而在<code>NodeJS</code>中,事件循环是基于<code>libuv</code>实现,<code>libuv</code>是一个多平台的专注于异步IO的库,如下图最右侧所示:</p><p><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534042708.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534042708.png" data-href="" style=""/></p><p>上图<code>EVENT_QUEUE</code> 给人看起来只有一个队列,但<code>EventLoop</code>存在6个阶段,每个阶段都有对应的一个先进先出的回调队列</p><h2>二、流程</h2><p>上节讲到事件循环分成了六个阶段,对应如下:</p><p><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534877720.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534877720.png" data-href="" style=""/></p><ul><li>timers阶段:这个阶段执行timer(setTimeout、setInterval)的回调</li><li>定时器检测阶段(timers):本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数</li><li>I/O事件回调阶段(I/O callbacks):执行延迟到下一个循环迭代的 I/O 回调,即上一轮循环中未被执行的一些I/O回调</li><li>闲置阶段(idle, prepare):仅系统内部使用</li><li>轮询阶段(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞</li><li>检查阶段(check):setImmediate() 回调函数在这里执行</li><li>关闭事件回调阶段(close callback):一些关闭的回调函数,如:socket.on('close', ...)</li></ul><p>每个阶段对应一个队列,当事件循环进入某个阶段时, 将会在该阶段内执行回调,直到队列耗尽或者回调的最大数量已执行, 那么将进入下一个处理阶段</p><p>除了上述6个阶段,还存在<code>process.nextTick</code>,其不属于事件循环的任何一个阶段,它属于该阶段与下阶段之间的过渡, 即本阶段执行结束, 进入下一个阶段前, 所要执行的回调,类似插队</p><p>流程图如下所示:</p><p><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710535979466.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710535979466.png" data-href="" style=""/></p><p>在<code>Node</code>中,同样存在宏任务和微任务,与浏览器中的事件循环相似</p><p>微任务对应有:</p><ul><li>next tick queue:process.nextTick</li><li>other queue:Promise的then回调、queueMicrotask</li></ul><p>宏任务对应有:</p><ul><li>timer queue:setTimeout、setInterval</li><li>poll queue:IO事件</li><li>check queue:setImmediate</li><li>close queue:close事件</li></ul><p>其执行顺序为:</p><ul><li>next tick microtask queue</li><li>other microtask queue</li><li>timer queue</li><li>poll queue</li><li>check queue</li><li>close queue</li></ul><h2>三、题目</h2><p>通过上面的学习,下面开始看看题目</p><pre><code class="language-js">async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') 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')); new Promise(function (resolve) { console.log('promise1') resolve(); console.log('promise2') }).then(function () { console.log('promise3') }) console.log('script end')</code></pre><p>分析过程:</p><ul><li>先找到同步任务,输出script start</li><li>遇到第一个 setTimeout,将里面的回调函数放到 timer 队列中</li><li>遇到第二个 setTimeout,300ms后将里面的回调函数放到 timer 队列中</li><li>遇到第一个setImmediate,将里面的回调函数放到 check 队列中</li><li>遇到第一个 nextTick,将其里面的回调函数放到本轮同步任务执行完毕后执行</li><li>执行 async1函数,输出 async1 start</li><li>执行 async2 函数,输出 async2,async2 后面的输出 async1 end进入微任务,等待下一轮的事件循环</li><li>遇到第二个,将其里面的回调函数放到本轮同步任务执行完毕后执行</li><li>遇到 new Promise,执行里面的立即执行函数,输出 promise1、promise2</li><li>then里面的回调函数进入微任务队列</li><li>遇到同步任务,输出 script end</li><li>执行下一轮回到函数,先依次输出 nextTick 的函数,分别是 nextTick1、nextTick2</li><li>然后执行微任务队列,依次输出 async1 end、promise3</li><li>执行timer 队列,依次输出 setTimeout0</li><li>接着执行 check 队列,依次输出 setImmediate</li><li>300ms后,timer 队列存在任务,执行输出 setTimeout2</li></ul><p>执行结果如下:</p><pre><code >script start async1 start async2 promise1 promise2 script end nextTick1 nextTick2 async1 end promise3 setTimeout0 setImmediate setTimeout2</code></pre><p>最后有一道是关于<code>setTimeout</code>与<code>setImmediate</code>的输出顺序</p><pre><code class="language-js">setTimeout(() => { console.log("setTimeout"); }, 0); setImmediate(() => { console.log("setImmediate"); });</code></pre><p>输出情况如下:</p><pre><code class="language-js">情况一: setTimeout setImmediate 情况二: setImmediate setTimeout</code></pre><p>分析下流程:</p><ul><li>外层同步代码一次性全部执行完,遇到异步API就塞到对应的阶段</li><li>遇到<code>setTimeout</code>,虽然设置的是0毫秒触发,但实际上会被强制改成1ms,时间到了然后塞入<code>times</code>阶段</li><li>遇到<code>setImmediate</code>塞入<code>check</code>阶段</li><li>同步代码执行完毕,进入Event Loop</li><li>先进入<code>times</code>阶段,检查当前时间过去了1毫秒没有,如果过了1毫秒,满足<code>setTimeout</code>条件,执行回调,如果没过1毫秒,跳过</li><li>跳过空的阶段,进入check阶段,执行<code>setImmediate</code>回调</li></ul><p>这里的关键在于这1ms,如果同步代码执行时间较长,进入<code>Event Loop</code>的时候1毫秒已经过了,<code>setTimeout</code>先执行,如果1毫秒还没到,就先执行了<code>setImmediate</code></p>
<h2>一、是什么</h2><p>在<a href="https://github.com/febobo/web-interview/issues/73" target="">浏览器事件循环</a>中,我们了解到<code>javascript</code>在浏览器中的事件循环机制,其是根据<code>HTML5</code>定义的规范来实现</p><p>而在<code>NodeJS</code>中,事件循环是基于<code>libuv</code>实现,<code>libuv</code>是一个多平台的专注于异步IO的库,如下图最右侧所示:</p><p><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534042708.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534042708.png" data-href="" style=""/></p><p>上图<code>EVENT_QUEUE</code> 给人看起来只有一个队列,但<code>EventLoop</code>存在6个阶段,每个阶段都有对应的一个先进先出的回调队列</p><h2>二、流程</h2><p>上节讲到事件循环分成了六个阶段,对应如下:</p><p><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534877720.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710534877720.png" data-href="" style=""/></p><ul><li>timers阶段:这个阶段执行timer(setTimeout、setInterval)的回调</li><li>定时器检测阶段(timers):本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数</li><li>I/O事件回调阶段(I/O callbacks):执行延迟到下一个循环迭代的 I/O 回调,即上一轮循环中未被执行的一些I/O回调</li><li>闲置阶段(idle, prepare):仅系统内部使用</li><li>轮询阶段(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞</li><li>检查阶段(check):setImmediate() 回调函数在这里执行</li><li>关闭事件回调阶段(close callback):一些关闭的回调函数,如:socket.on('close', ...)</li></ul><p>每个阶段对应一个队列,当事件循环进入某个阶段时, 将会在该阶段内执行回调,直到队列耗尽或者回调的最大数量已执行, 那么将进入下一个处理阶段</p><p>除了上述6个阶段,还存在<code>process.nextTick</code>,其不属于事件循环的任何一个阶段,它属于该阶段与下阶段之间的过渡, 即本阶段执行结束, 进入下一个阶段前, 所要执行的回调,类似插队</p><p>流程图如下所示:</p><p><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710535979466.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710535979466.png" data-href="" style=""/></p><p>在<code>Node</code>中,同样存在宏任务和微任务,与浏览器中的事件循环相似</p><p>微任务对应有:</p><ul><li>next tick queue:process.nextTick</li><li>other queue:Promise的then回调、queueMicrotask</li></ul><p>宏任务对应有:</p><ul><li>timer queue:setTimeout、setInterval</li><li>poll queue:IO事件</li><li>check queue:setImmediate</li><li>close queue:close事件</li></ul><p>其执行顺序为:</p><ul><li>next tick microtask queue</li><li>other microtask queue</li><li>timer queue</li><li>poll queue</li><li>check queue</li><li>close queue</li></ul><h2>三、题目</h2><p>通过上面的学习,下面开始看看题目</p><pre><code class="language-js">async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') 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')); new Promise(function (resolve) { console.log('promise1') resolve(); console.log('promise2') }).then(function () { console.log('promise3') }) console.log('script end')</code></pre><p>分析过程:</p><ul><li>先找到同步任务,输出script start</li><li>遇到第一个 setTimeout,将里面的回调函数放到 timer 队列中</li><li>遇到第二个 setTimeout,300ms后将里面的回调函数放到 timer 队列中</li><li>遇到第一个setImmediate,将里面的回调函数放到 check 队列中</li><li>遇到第一个 nextTick,将其里面的回调函数放到本轮同步任务执行完毕后执行</li><li>执行 async1函数,输出 async1 start</li><li>执行 async2 函数,输出 async2,async2 后面的输出 async1 end进入微任务,等待下一轮的事件循环</li><li>遇到第二个,将其里面的回调函数放到本轮同步任务执行完毕后执行</li><li>遇到 new Promise,执行里面的立即执行函数,输出 promise1、promise2</li><li>then里面的回调函数进入微任务队列</li><li>遇到同步任务,输出 script end</li><li>执行下一轮回到函数,先依次输出 nextTick 的函数,分别是 nextTick1、nextTick2</li><li>然后执行微任务队列,依次输出 async1 end、promise3</li><li>执行timer 队列,依次输出 setTimeout0</li><li>接着执行 check 队列,依次输出 setImmediate</li><li>300ms后,timer 队列存在任务,执行输出 setTimeout2</li></ul><p>执行结果如下:</p><pre><code >script start async1 start async2 promise1 promise2 script end nextTick1 nextTick2 async1 end promise3 setTimeout0 setImmediate setTimeout2</code></pre><p>最后有一道是关于<code>setTimeout</code>与<code>setImmediate</code>的输出顺序</p><pre><code class="language-js">setTimeout(() => { console.log("setTimeout"); }, 0); setImmediate(() => { console.log("setImmediate"); });</code></pre><p>输出情况如下:</p><pre><code class="language-js">情况一: setTimeout setImmediate 情况二: setImmediate setTimeout</code></pre><p>分析下流程:</p><ul><li>外层同步代码一次性全部执行完,遇到异步API就塞到对应的阶段</li><li>遇到<code>setTimeout</code>,虽然设置的是0毫秒触发,但实际上会被强制改成1ms,时间到了然后塞入<code>times</code>阶段</li><li>遇到<code>setImmediate</code>塞入<code>check</code>阶段</li><li>同步代码执行完毕,进入Event Loop</li><li>先进入<code>times</code>阶段,检查当前时间过去了1毫秒没有,如果过了1毫秒,满足<code>setTimeout</code>条件,执行回调,如果没过1毫秒,跳过</li><li>跳过空的阶段,进入check阶段,执行<code>setImmediate</code>回调</li></ul><p>这里的关键在于这1ms,如果同步代码执行时间较长,进入<code>Event Loop</code>的时候1毫秒已经过了,<code>setTimeout</code>先执行,如果1毫秒还没到,就先执行了<code>setImmediate</code></p>
评论 (
0
)
登录
后才可以发表评论
状态
待办的
待办的
进行中
已完成
已关闭
负责人
未设置
标签
Node
未设置
标签管理
里程碑
未关联里程碑
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
未关联
未关联
master
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
参与者(1)
1
https://gitee.com/DreamCoders/CoderGuide.git
git@gitee.com:DreamCoders/CoderGuide.git
DreamCoders
CoderGuide
CoderGuide
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册