深入浅析Node事件循环中的微任务队列

image.png

在 之前的文章 中,我们了解到事件循环是 Node.js 的一个关键部分,用于协调同步和异步代码的执行。

它由六个不同的队列组成。一个 nextTick 队列和一个 Promise 队列(被称为微任务队列)、一个计时器队列、一个 I/O 队列、一个检查队列,最后是关闭队列。【相关教程推荐:nodejs视频教程、编程教学】

在每次循环中,回调函数会在适当时出队(dequeued)并在调用栈上执行。从本文开始,我们会进行一些实验以确保我们对事件循环的理解是正确的。

我们的第一组实验,将重点关注 nextTick 队列 和 Promise 队列。但在深入实验之前,需要先了解如何将回调函数排到队列中。

注意:所有实验都是使用 CommonJS 模块格式进行的。

入队回调函数

要将回调函数加入到 nextTick 队列中,我们可以使用内置的 process.nextTick() 方法。语法很简单:process.nextTick(callbackFn)。当该方法在调用栈上执行时,回调函数将被加入到 nextTick 队列中。

要将回调函数加入到 Promise 队列中,我们需要使用 Promise.resolve().then(callbackFn)。当 Promise 解决时(resolve),传递给 then() 的回调函数将被入队到 Promise 队列中。

现在我们已经了解了如何向两个队列添加回调函数,让我们开始第一个实验吧。

实验一

代码

// index.jsconsole.log("console.log 1");process.nextTick(() => console.log("this is process.nextTick 1"));console.log("console.log 2");

这段代码,记录了三个不同的语句。第二个语句使用 process.nextTick() 将回调函数排入 nextTick 队列。

可视化

assets_YJIGb4i01jvw0SRdL5Bt_b32c606155c14ecfa60c8dc102f3bcf0_compressed.gif

第一个 console.log() 语句被推入调用栈中执行。它在控制台中记录相应的消息,然后弹出堆栈。

接下来,在调用栈上执行 process.nextTick(),将回调函数入队到 nextTick 队列,并弹出。此时仍有用户编写的代码需要执行,因此回调函数必须等待其轮到。

执行继续进行,最后一个 console.log() 语句被推入堆栈中,该消息记录在控制台中,并将该函数从调用栈中弹出。现在,没有更多的用户编写同步代码需要执行,因此控制权进入事件循环。

nextTick 队列中的回调函数被推入调用栈,console.log() 被推入调用栈并且执行,在控制台记录相应的消息。

console.log 1console.log 2this is process.nextTick 1

推论

所有用户编写的同步 JavaScript 代码优先于异步代码执行。

让我们继续进行第二个实验。

实验二

代码

// index.jsPromise.resolve().then(() => console.log("this is Promise.resolve 1"));process.nextTick(() => console.log("this is process.nextTick 1"));

我们有一个 Promise.resolve().then() 调用和一个 process.nextTick() 调用。

可视化

assets_YJIGb4i01jvw0SRdL5Bt_b32c606155c14ecfa60c8dc102f3bcf0_compressed (1).gif

当调用栈执行第 1 行时,它将回调函数排队到 Promise 队列中;当调用栈执行第 2 行时,它将回调函数排队到 nextTick 队列中。

第 2 行后没有代码需要执行。

控制权进入事件循环,在其中 nextTick 队列优先于 Promise 队列(这是 Node.js 运行时的工作方式)。

事件循环执行 nextTick 队列回调函数,然后再执行 Promise 队列回调函数。

小微助手 小微助手

微信推出的一款专注于提升桌面效率的助手型AI工具

小微助手 47 查看详情 小微助手

控制台显示“this is process.nextTick 1”,然后是“this is Promise.resolve 1”。

this is process.nextTick 1this is Promise.resolve 1

推论

nextTick 队列中的所有回调函数优先于 Promise 队列中的回调函数执行。

让我带你走一遍上述第二个实验的更详细版本。

福利实验

代码

// index.jsprocess.nextTick(() => console.log("this is process.nextTick 1"));process.nextTick(() => {  console.log("this is process.nextTick 2");  process.nextTick(() =>    console.log("this is the inner next tick inside next tick")  );});process.nextTick(() => console.log("this is process.nextTick 3"));Promise.resolve().then(() => console.log("this is Promise.resolve 1"));Promise.resolve().then(() => {  console.log("this is Promise.resolve 2");  process.nextTick(() =>    console.log("this is the inner next tick inside Promise then block")  );});Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

该代码包含三个 process.nextTick() 调用和三个 Promise.resolve() 调用语句。每个回调函数记录适当的消息。

但是,第二个 process.nextTick() 和第二个 Promise.resolve() 都有一个额外的 process.nextTick() 语句,每个都带有一个回调函数。

可视化

assets_YJIGb4i01jvw0SRdL5Bt_b32c606155c14ecfa60c8dc102f3bcf0_compressed (2).gif

为了加快对此可视化的解释,我将省略调用栈讲解。当调用栈执行所有 6 个语句时,nextTick 队列中有 3 个回调函数,Promise 队列中也有 3 个回调函数。没有其他要执行的内容后,控制权进入事件循环。

我们知道,nextTick 队列中具有高优先级。首先执行第 1 个回调函数,并将相应的消息记录到控制台中。

接下来,执行第 2 个回调函数,记录了第 2 条日志语句。但是,这个回调函数包含另一个对 process.nextTick() 的调用,该方法内的回调函数(译注:即() => console.log("this is the inner next tick inside next tick"))入队到nextTick 队列末尾。

然后 Node.js 执行第 3 个回调函数并将相应消息记录到控制台上。最初只有 3 个回调,但是因为第 2 次时向nextTick 队列又添加了一个,所以变成 4 个了。

事件循环将第 4 个回调函数推入调用栈,执行 console.log() 语句。

接着处理完 nextTick 队列之后,控制流程进入到 Promise 队列。Promise 队列与 nextTick 队列处理方式类似。

首先记录“Promise.resolve 1”,然后是“Promise.resolve 2”,这时因为调用 process.nextTick() 的原因,一个函数(译注:即 () => console.log("this is the inner next tick inside Promise then block") ) 被推入 nextTick 队列了。尽管如此,控制流程仍停留在 Promise 队列,因此还会继续执行队列中的其他函数。然后我们得到“Promise.resolve 3”,此时 Promise 队列为空。

Node.js 将再次检查微任务队列中是否有新的回调。由于 nextTick 队列中有一个回调,因此会执行这个回调,导致我们的最后一条日志输出。

this is process.nextTick 1this is process.nextTick 2this is process.nextTick 3this is the inner next tick inside next tickthis is Promise.resolve 1this is Promise.resolve 2this is Promise.resolve 3this is the inner next tick inside Promise then block

这可能是一个稍微高级的实验,但推论仍然相同。

推论

nextTick 队列中的所有回调函数优先于 Promise 队列中的回调函数执行。

使用 process.nextTick() 时要小心。过度使用此方法可能会导致事件循环饥饿,从而阻止队列中的其余部分运行。当存在大量的 nextTick() 调用时,I/O 队列是无法执行自己的回调函数的。官方文档建议使用 process.nextTick() 的两个主要场景:处理错误或在调用栈为空事件循环继续之前执行回调用。所以在使用 process.nextTick() 时,要明智一些。

总结

实验表明,用户编写的所有同步 JavaScript 代码优先于异步代码执行,并且 nextTick 队列中的所有回调函数优先于 Promise 队列中的回调函数执行。

原文链接:Visualizing nextTick and Promise Queues in Node.js Event Loop,2023年3月30日,by Vishwas Gopinath

更多node相关知识,请访问:nodejs 教程!

以上就是深入浅析Node事件循环中的微任务队列的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/541821.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 15:38:10
下一篇 2025年11月9日 15:38:57

相关推荐

  • Vue.js应用中配置环境变量:灵活管理后端通信地址

    在%ignore_a_1%应用中,灵活配置后端api地址等参数是开发与部署的关键。本文将详细介绍两种主要的环境变量配置方法:推荐使用的`.env`文件,以及通过`cross-env`库在命令行中设置环境变量。通过这些方法,开发者可以轻松实现开发、测试、生产等不同环境下配置的动态切换,提高应用的可维护…

    2025年12月6日 web前端
    000
  • VSCode选择范围提供者实现

    Selection Range Provider是VSCode中用于实现层级化代码选择的API,通过注册provideSelectionRanges方法,按光标位置从内到外逐层扩展选择范围,如从变量名扩展至函数体;需结合AST解析构建准确的SelectionRange链式结构以提升选择智能性。 在 …

    2025年12月6日 开发工具
    000
  • JavaScript动态生成日历式水平日期布局的优化实践

    本教程将指导如何使用javascript高效、正确地动态生成html表格中的日历式水平日期布局。重点解决直接操作`innerhtml`时遇到的标签闭合问题,通过数组构建html字符串来避免浏览器解析错误,并利用事件委托机制优化动态生成元素的事件处理,确保生成结构清晰、功能完善的日期展示。 在前端开发…

    2025年12月6日 web前端
    000
  • JavaScript响应式编程与Observable

    Observable是响应式编程中处理异步数据流的核心概念,它允许随时间推移发出多个值,支持订阅、操作符链式调用及统一错误处理,广泛应用于事件监听、状态管理和复杂异步逻辑,提升代码可维护性与可读性。 响应式编程是一种面向数据流和变化传播的编程范式。在前端开发中,尤其面对复杂的用户交互和异步操作时,J…

    2025年12月6日 web前端
    000
  • JavaScript生成器与迭代器协议实现

    生成器和迭代器基于统一协议实现惰性求值与数据遍历,通过next()方法返回{value, done}对象,生成器函数简化了迭代器创建过程,提升处理大数据序列的效率与代码可读性。 JavaScript中的生成器(Generator)和迭代器(Iterator)是处理数据序列的重要机制,尤其在处理惰性求…

    2025年12月6日 web前端
    000
  • VSCode入门:基础配置与插件推荐

    刚用VSCode,别急着装一堆东西。先把基础设好,再按需求加插件,效率高还不卡。核心就三步:界面顺手、主题舒服、功能够用。 设置中文和常用界面 打开软件,左边活动栏有五个图标,点最下面那个“扩展”。搜索“Chinese”,装上官方出的“Chinese (Simplified) Language Pa…

    2025年12月6日 开发工具
    000
  • VSCode性能分析与瓶颈诊断技术

    首先通过资源监控定位异常进程,再利用开发者工具分析性能瓶颈,结合禁用扩展、优化语言服务器配置及项目设置,可有效解决VSCode卡顿问题。 VSCode作为主流的代码编辑器,虽然轻量高效,但在处理大型项目或配置复杂扩展时可能出现卡顿、响应延迟等问题。要解决这些性能问题,需要系统性地进行性能分析与瓶颈诊…

    2025年12月6日 开发工具
    000
  • VSCode的悬浮提示信息可以自定义吗?

    可以通过JSDoc、docstring和扩展插件自定义VSCode悬浮提示内容,如1. 添加JSDoc或Python docstring增强信息;2. 调整hover延迟与粘性等显示行为;3. 使用支持自定义提示的扩展或开发hover provider实现深度定制,但无法直接修改HTML结构或手动编…

    2025年12月6日 开发工具
    000
  • 优化PDF中下载链接的URL显示:利用HTML title 属性

    在pdf文档中,当包含下载链接时,完整的url路径通常会在鼠标悬停时或直接显示在链接文本中,这可能不符合预期。本文将探讨为何传统方法如`.htaccess`重写或javascript不适用于pdf环境,并提出一种利用html “ 标签的 `title` 属性来定制链接悬停显示文本的解决方…

    2025年12月6日 后端开发
    000
  • Phaser 3 游戏画布响应式适配:保持高度控制宽度

    本文旨在提供一种在 Phaser 3 游戏中实现画布响应式适配的方案,核心思路是利用 `Phaser.Scale.HEIGHT_CONTROLS_WIDTH` 缩放模式,使画布高度适应父容器,宽度随之调整,并始终居中显示。这种方法适用于需要保持游戏核心内容在屏幕中央,允许左右裁剪的场景。 在 Pha…

    2025年12月6日 web前端
    000
  • 洋葱浏览器下载文件安全吗_使用洋葱浏览器安全下载文件的注意事项

    首先验证.onion链接真实性,通过可信渠道获取并核对PGP签名;其次在虚拟机或沙盒中下载,关闭共享功能并校验文件哈希;接着使用多引擎扫描工具检测恶意代码,分析行为日志;最后严格管理浏览器权限,禁用JavaScript和第三方插件,定期清除痕迹。 如果您尝试通过洋葱浏览器下载文件,但对来源和操作方式…

    2025年12月6日 软件教程
    000
  • 分布式系统下的JavaScript消息队列实现

    答案:在Node.js中通过集成RabbitMQ或Kafka实现分布式系统消息通信。使用amqplib连接RabbitMQ,创建通道并声明交换机与队列,通过publish发送、consume接收消息,保障可靠性与解耦;或采用kafkajs连接Kafka集群,生产者向topic发消息,消费者订阅处理,…

    2025年12月6日 web前端
    000
  • MySQL模糊查询:高效处理含空格和多格式电话号码

    在mysql数据库中,当电话号码字段包含多种格式和空格时,传统的`like`查询可能无法返回预期结果。本文将介绍如何利用`replace`函数在查询时动态移除电话号码中的空格,从而实现准确的模糊匹配。同时,我们还将探讨性能考量及数据标准化等最佳实践,帮助您优化数据库查询和数据质量。 挑战:含空格电话…

    2025年12月6日 后端开发
    000
  • Via浏览器为什么无法上传图片或文件_Via浏览器上传文件失败的原因及解决方法

    Via浏览器上传失败可因权限、设置或兼容性问题导致,需检查存储权限、启用JavaScript、更换User-Agent、使用系统文件选择器或清除缓存解决。 如果您在使用Via浏览器尝试上传图片或文件时遇到失败提示,可能是由于权限设置、浏览器配置或网页兼容性问题导致。此类问题通常可以通过调整设置或更换…

    2025年12月6日 电脑教程
    000
  • JavaScript语音识别与合成

    答案:JavaScript通过Web Speech API实现语音识别与合成。使用SpeechRecognition将语音转文本,需在安全上下文并获麦克风权限;利用SpeechSynthesis将文本转语音,可设置语速、音调等参数。两者结合可用于语音助手、无障碍阅读等场景,但语音识别兼容性有限,主要…

    2025年12月6日 web前端
    000
  • JavaScript持续集成与部署

    持续集成与部署(CI/CD)通过自动化测试、构建和部署提升JavaScript项目交付效率。1. CI指频繁合并代码并自动运行测试以快速发现错误;2. CD在CI通过后自动将应用部署至生产环境;3. 常用工具包括GitHub Actions、GitLab CI/CD、CircleCI和Jenkins…

    2025年12月6日 web前端
    000
  • 解决HTML锚点链接页面重载与URL路径丢失问题

    在使用html锚点链接进行页面内部导航时,开发者可能会遇到页面意外重载或url路径丢失的问题,导致无法正确滚动到目标区域。本文将深入探讨这一常见问题的根源,并提供一种简洁有效的解决方案:确保锚点链接的`href`属性包含当前页面的完整相对路径,从而实现平滑的页面内跳转,避免不必要的页面刷新和url结…

    2025年12月6日 后端开发
    000
  • JavaScript数据可视化进阶

    答案是%ignore_a_1%进阶需以叙事为核心,结合工具深度与交互设计。首先理解场景,选用D3.js、Chart.js或ECharts等工具,挖掘其数据驱动、动态更新与插件扩展能力;其次优化性能,通过Web Workers、LTTB算法和Canvas渲染处理大规模数据;再者增强交互,实现跨图表联动…

    2025年12月6日 web前端
    000
  • PDF文档中隐藏下载链接真实路径的教程

    本教程旨在解决pdf文档中下载链接显示完整url路径的问题,尤其是在鼠标悬停时暴露动态参数。文章将解释为何传统的.htaccess重写或javascript方法不适用于pdf环境,并提出一种利用html “标签的`title`属性来控制链接提示文本的有效策略,从而在不影响功能的前提下,优…

    2025年12月6日 后端开发
    000
  • JavaScript SVG动态矢量图形处理

    JavaScript结合SVG可实现高效动态图形处理,通过createElementNS创建带命名空间的SVG元素,动态生成如圆形等图形;利用setAttribute实时修改属性实现交互响应;借助requestAnimationFrame或事件驱动完成平滑动画;基于数据映射生成路径,支持折线图等复杂…

    2025年12月6日 web前端
    000

发表回复

登录后才能评论
关注微信