Node.js 事件循环:定时任务、延迟任务和 I/O 事件的艺术

在这里插入图片描述

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6
🍨 阿珊和她的猫_CSDN个人主页
🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》
🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入门到实战全面掌握 uni-app》

文章目录

  • 定时器和延时器
    • 使用 setTimeout 和 setInterval 实现定时任务
    • 解释定时器和延时器在事件循环中的工作原理
  • I/O 事件
    • 文件操作和网络请求等 I/O 事件的处理
    • 理解 I/O 事件在事件循环中的优先级和回调函数的执行时机
  • 拓展话题:Promise 和 Async/Await
    • 介绍 Promise 和 Async/Await 与事件循环的关系
    • 如何使用 Promise 和 Async/Await 优化事件循环的性能
  • 总结与展望
    • 总结 Node.js 事件循环的核心概念和工作原理

定时器和延时器

使用 setTimeout 和 setInterval 实现定时任务

在 Node.js 中,你可以使用 setTimeoutsetInterval 函数来实现定时任务。
这两个函数都接受一个回调函数作为参数,并在指定的时间后执行该回调函数。

以下是使用 setTimeoutsetInterval 实现定时任务的示例代码:

// 使用 setTimeout 实现定时任务
setTimeout(() => {console.log('定时任务执行');
}, 1000); // 1000 毫秒后执行回调函数// 使用 setInterval 实现定时任务
setInterval(() => {console.log('每隔 1 秒执行定时任务');
}, 1000); // 每隔 1 秒执行回调函数

在上面的示例中,我们使用 setTimeout 函数设置了一个定时任务,它将在 1 秒后执行。然后,我们使用 setInterval 函数设置了一个每隔 1 秒执行一次的定时任务。

请注意,setTimeoutsetInterval 函数返回一个定时器标识符,你可以使用这个标识符来取消定时任务。例如:

let timerId = setTimeout(() => {console.log('定时任务执行');
}, 1000);clearTimeout(timerId); // 取消定时任务

在上面的示例中,我们使用 clearTimeout 函数取消了之前设置的定时任务。同样,你也可以使用 clearInterval 函数来取消 setInterval 设置的定时任务。

解释定时器和延时器在事件循环中的工作原理

定时器(setTimeoutsetInterval)和延时器(setImmediatesetTimeout函数的第二个参数)在事件循环中以不同的方式工作。

  1. setTimeoutsetInterval:这两个函数将回调函数添加到定时器事件队列(timers)中。当事件循环到达 timers 阶段时,它会按照到期时间的顺序执行回调函数。

    • setTimeout 函数接受两个参数:回调函数和延迟时间(以毫秒为单位)。它将回调函数添加到定时器事件队列中,并在指定的延迟时间后执行。
    • setInterval 函数与 setTimeout 类似,除了它会在每次回调函数执行后重新将其添加到定时器事件队列中,以实现周期性执行。
  2. setImmediate:这个函数将回调函数添加到 setImmediate 队列中。setImmediate 队列中的回调函数会在事件循环的 check 阶段被执行,并且在 timers 队列之前。

    • setTimeout 不同,setImmediate 的回调函数会在当前事件循环迭代中尽快执行,而不是在特定的延迟时间后执行。

    • 由于 setImmediate 队列在 timers 队列之前执行,因此如果同时使用 setTimeoutsetImmediatesetImmediate 队列中的回调函数会先执行。

需要注意的是,setImmediate 并不是在所有环境中都被支持,它是 Node.js 中的一个扩展,而在浏览器环境中不被支持。在浏览器中,可以使用 Promisethen 方法或 MutationObserver 来实现类似的立即执行功能。

I/O 事件

文件操作和网络请求等 I/O 事件的处理

文件操作和网络请求等 I/O 事件的处理在事件循环中通过 I/O 事件队列(I/O)进行处理。

当进行文件操作或发起网络请求时,Node.js 会将相应的 I/O 事件添加到 I/O 事件队列中。

事件循环会在 I/O 阶段检查 I/O 事件队列是否有事件。如果有事件,事件循环会从队列中取出事件并执行相应的回调函数。在文件操作中,例如读取文件或写入文件,回调函数将在文件操作完成后被执行。在网络请求中,例如 HTTP 请求,回调函数将在响应数据可用时被执行。

I/O 事件的处理是非阻塞的,这意味着在执行 I/O 操作时,事件循环不会被阻塞
相反,它会继续处理其他队列中的事件
当 I/O 操作完成并将事件添加到 I/O 事件队列中时,事件循环会在适当的时候处理这些事件。

这种非阻塞 I/O 模型使得 Node.js 能够高效地处理大量的 I/O 操作,因为它不会因为等待 I/O 操作完成而阻塞事件循环。
相反,它可以同时处理多个 I/O 操作,并在它们完成时逐个处理它们的回调函数。

理解 I/O 事件在事件循环中的优先级和回调函数的执行时机

在事件循环中,I/O 事件的优先级与其他事件队列(如定时器事件队列和 setImmediate 队列)的优先级不同。

具体来说,I/O 事件的优先级较低,它们会在其他事件队列中的事件处理完毕后被处理。

回调函数的执行时机取决于事件的类型和处理方式。对于文件操作和网络请求等 I/O 事件,回调函数将在文件操作完成或响应数据可用时被执行。在文件操作中,例如读取文件或写入文件,回调函数将在文件操作完成后被执行。在网络请求中,例如 HTTP 请求,回调函数将在响应数据可用时被执行。

需要注意的是,I/O 事件的处理是非阻塞的,这意味着在执行 I/O 操作时,事件循环不会被阻塞。相反,它会继续处理其他队列中的事件。当 I/O 操作完成并将事件添加到 I/O 事件队列中时,事件循环会在适当的时候处理这些事件。

这种非阻塞 I/O 模型使得 Node.js 能够高效地处理大量的 I/O 操作,因为它不会因为等待 I/O 操作完成而阻塞事件循环。相反,它可以同时处理多个 I/O 操作,并在它们完成时逐个处理它们的回调函数。

拓展话题:Promise 和 Async/Await

介绍 Promise 和 Async/Await 与事件循环的关系

Promise 和 Async/Await 是 JavaScript 中的异步编程解决方案,它们与事件循环密切相关。

Promise 是一种用于处理异步操作结果的对象。它可以表示一个异步操作的最终完成(fulfillment)或失败(rejection)。Promise 对象有一个 then 方法,用于注册在异步操作完成后执行的回调函数。

在事件循环中,Promise 的处理通过 Promise 队列(promise)进行。当创建一个 Promise 对象时,它会被添加到 Promise 队列中。事件循环会在 Promise 阶段检查 Promise 队列是否有等待处理的 Promise 对象。如果有,事件循环会取出队列中的 Promise 对象并执行其回调函数。

Async/Await 是基于 Promise 的语法糖,它使得异步代码的编写更加简洁和直观。使用 Async/Await,你可以将异步操作编写为类似同步代码的形式,通过使用 async 关键字定义异步函数,并使用 await 关键字等待 Promise 的完成。

在 Async/Await 的情况下,异步操作的执行仍然通过 Promise 进行,但异步函数的执行会被暂停,直到等待的 Promise 对象完成。当遇到 await 关键字时,JavaScript 引擎会将异步函数的执行挂起,并将其添加到微任务队列(microtask)中。事件循环会在 microtask 阶段检查微任务队列,并执行其中的微任务。

因此,Promise 和 Async/Await 都是 JavaScript 中处理异步操作的方式,它们通过事件循环来协调异步操作的执行。Promise 通过 Promise 队列进行处理,而 Async/Await 通过将异步函数的执行挂起并添加到微任务队列中来实现异步操作的等待和处理。事件循环会按照特定的顺序处理这些队列中的事件,以实现异步代码的非阻塞执行。

如何使用 Promise 和 Async/Await 优化事件循环的性能

使用 Promise 和 Async/Await 可以在一定程度上优化事件循环的性能。

以下是一些建议:

在这里插入图片描述

  1. 使用 Promise.all:如果你有多个异步操作需要同时完成,可以使用 Promise.all 方法。它接受一个 Promise 对象数组作为参数,并返回一个新的 Promise 对象,该对象在所有传入的 Promise 对象都完成后才会完成。这样可以避免多个异步操作的回调函数在事件循环中被多次处理,提高性能。

  2. 使用 Async/Await: Async/Await 是基于 Promise 的语法糖,它使得异步代码的编写更加简洁和直观。使用 Async/Await 可以避免回调地狱的问题,并使代码更易于阅读和维护。在使用 Async/Await 时,注意将异步操作放在 async 函数中,并使用 await 关键字等待 Promise 的完成。

  3. 避免嵌套的异步操作:过多的嵌套异步操作可能会导致事件循环的性能下降。尽量保持异步操作的层次结构简单,并避免在回调函数中再次进行异步操作。如果需要处理嵌套的异步操作,可以使用 Promise 的链式调用或使用 async/await 来简化代码。

  4. 使用微任务:在 Async/Await 中,遇到 await 关键字时,JavaScript 引擎会将异步函数的执行挂起,并将其添加到微任务队列中。微任务队列会在事件循环的 microtask 阶段被处理。利用微任务队列可以实现一些性能优化,例如在微任务队列中进行一些轻量级的操作,以避免阻塞事件循环。

  5. 合理使用 Promise 的 resolve 和 reject:在创建 Promise 对象时,尽量避免在 Promise 的构造函数中传入立即 resolve 或 reject 的值。这可能会导致不必要的微任务被添加到微任务队列中,从而影响性能。尽量在需要时才 resolve 或 reject Promise 对象。

通过合理使用 Promise 和 Async/Await,可以使事件循环的性能得到一定程度的优化。但具体的优化效果还需要根据实际情况进行评估和测试,以确保在你的应用中获得最佳性能。

总结与展望

总结 Node.js 事件循环的核心概念和工作原理

Node.js 事件循环的核心概念和工作原理可以总结如下:

  1. 事件循环:Node.js 是基于事件驱动的非阻塞 I/O 模型。事件循环是 Node.js 处理事件的核心机制,它负责监听和处理各种事件,并在需要时执行相应的回调函数。

  2. 事件队列:事件循环使用事件队列来管理事件。有不同类型的事件队列,包括定时器事件队列、I/O 事件队列和 setImmediate 队列。这些队列按照特定的顺序进行处理。

  3. 阶段:事件循环分为多个阶段,包括 timers 阶段、I/O 阶段和 poll 阶段等。每个阶段会处理特定类型的事件。

  4. 回调函数:当事件发生时,相应的回调函数会被添加到事件队列中。事件循环会在适当的阶段取出队列中的回调函数并执行它们。

  5. 非阻塞 I/O:Node.js 使用非阻塞 I/O 模型,这意味着在执行 I/O 操作时,事件循环不会被阻塞。相反,它会将 I/O 操作委托给操作系统,并在 I/O 操作完成后触发相应的事件。

  6. 异步编程:由于 Node.js 的非阻塞特性,异步编程成为常见的编程方式。使用异步函数和回调函数可以在不阻塞事件循环的情况下执行异步操作。

  7. 定时器和延时器:定时器和延时器是在事件循环中管理时间相关操作的重要工具。定时器会在指定的时间后将回调函数添加到定时器事件队列中,而延时器会在当前事件循环迭代结束后将回调函数添加到下一个事件循环迭代的 poll 阶段。

通过事件循环,Node.js 能够高效地处理大量的 I/O 操作和并发请求,提供了一种高效的异步编程模型。了解事件循环的工作原理对于编写高效和可靠的 Node.js 应用至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/227565.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】获取调用者信息

水文一篇(毕竟已经有很多相关内容)。 暂时找到两个方式获取调用者信息,一个是traceback,另一个是inspect。 其中, inspect能获取更为详尽的上下文信息(?),在某些特殊场合甚至能以此省略函数调用时传入的locals()和globals()。 代…

位运算总结

文章目录 🍈1. 基础位运算🍌2. 给一个数n,确定它的二进制表示中的第x位是0还是1🍏3. 将一个数n的二进制表示的第x位修改成1🍓4. 将一个数的n的二进制表示的第x位修改成0🥔5. 位图的思想🫒6. 提前…

python服装电商系统vue购物商城django-pycharm毕业设计项目推荐

系统面向的使用群体为商家和消费者,商家和消费者所承担的功能各不相同,所对象的权限也各不相同。对于消费者和商家设计的功能如下: 对于消费者设计了五大功能模块: (1) 商品信息:用户可在商品…

MySQL优化技巧

在使用一些常规的 SQL 时&#xff0c;如果我们通过一些方法和技巧来优化这些 SQL 的实现&#xff0c;在性能上就会比使用常规通用的实现方式更 优化分页查询 通常我们是使用 <LIMIT M,N> 合适的 order by 来实现分页查询&#xff0c;这种实现方式在没有任何索引条件支…

Java中的多态究竟是什么?

目录 一.概念二.使用条件三.重写1.概念2.使用条件3.与重载对比4.举例5.为什么需要重写1.重写规则 2.静态绑定--重载3.动态绑定--重写 四.向上转型第一种传参方式&#xff1a;直接赋值第二种传参方式&#xff1a;通过传参优缺点 五.向下转型举例缺点 六.多态的优缺点优点缺点 一…

解锁 ElasticJob 云原生实践的难题

发生了什么 最近在逛 ElasticJob 官方社区时发现很多小伙伴都在头疼这个 ElasticJob 上云的问题&#xff0c;ElasticJob 本就号称分布式弹性任务调度框架&#xff0c;怎么在云原生环境就有了问题了呢&#xff0c;这就要从 Kubenertes 和 ElasticJob 的一些状态化说起。 有意思的…

windows10系统更新失败无法进入系统

用户反馈早上电脑重启&#xff0c;系统在更新卡好久好进去是否更新windows11&#xff0c;选否&#xff0c;重新就反复在更新中无法进入系统。我在测试的过程也是多次更新卡好久无法进入系统&#xff0c;而且出现下面提示 windows10系统更新失败无法进入系统&#xff0c;蓝屏提…

sqli-labs(4)

17. 观察页面发现是一个用来更改用户密码的页面&#xff0c;页面中出现了用户Dhakkan user输入Dhakkan passs输入1发现成功找注入点 先在user尝试&#xff0c;发现不管输入什么都失败在pass中尝试注入 在pass中输入1‘ 报错注入 1 and extractvalue(1,concat(0x5c,database(…

【Electron】上下键切换消息

需求&#xff1a; 如图&#xff0c;需要监听上下键切换消息 Electron 注册 全局快捷键【globalShortcut】监听 在focus注册 在blur 注销 如苹果系统在使用某个软件(focus)时 右上角会有应用标题 Electron 代码&#xff1a; win.on(focus, ()>{globalShortcut.register(U…

OPENWRT解决配置pppoe后无法光猫路由管理界面

一、新建一个wan口 二、设置流量转发 设置完成后保存应用即可

Java基于SpringBoot+vue的租房网站设计与实现(V2.0)

文章目录 一、前言介绍二、主要技术三、系统设计&#xff08;部分&#xff09;3.1、主要功能模块设计3.2、系统登录设计 四、数据库设计&#xff08;部分&#xff09;五、运行截图5.1、 **管理员** **登录****5.2、管理员功能模块**5.2.1、用户管理5.2.2、房屋类型管理5.2.3、房…

阿里云语雀频繁崩溃,有什么文档管理工具是比较稳定的?

10月23 日14:00左右&#xff0c;蚂蚁集团旗下的在线文档编辑与协同工具语雀发生服务器故障&#xff0c;在线文档和官网都无法打开。直到当天晚上22:24&#xff0c;语雀服务才全部恢复正常。从故障发生到完全恢复正常&#xff0c;语雀整个宕机时间将近 8 小时&#xff0c;如此长…