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.mfbz.cn/a/197216.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python多线程和多进程

1.多线程 线程是程序执行的最小单位,一个进程至少有一个线程。 提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。 进程之间不能共享内存,但线程之间共享内存非常容易。 Python 常用的多线程库有threading 和…

微信小程序Vue+nodejs教室自习室座位预约系统68u2m

本文从管理员、用户的功能要求出发,教室预约系统小程序中的功能模块主要是实现管理端;首页、个人中心、教室信息管理、教室设备管理、用户管理、教室预约管理、管理员管理、系统管理,微信端;首页、教室信息、教室设备、教室预约、…

【吞噬星空】弧刀盘价值180亿,购买1016名强者,保卫地球

Hello,小伙伴们,我是拾荒君。 国漫《吞噬星空》的第95集更新了,一更新,我和我的小伙伴们就迫不及待地去观看了。在这个集剧中,罗峰在一个奴隶拍卖场中深切地感受到了宇宙中弱肉强食的残酷现实。他看到,在宇宙中&#…

goweb入门教程

本文是作者自己学习goweb时写的笔记,分享给大家,希望能有些帮助 前言: 关于web:本质 ​ ​ web中最重要的就是浏览器和服务器的request(请求)和response(响应); ​ 一个请求对应一个响应。 一个请求对应一个响应&…

鸿蒙开发ArkUI -常用布局

线性布局(Row/Column) 间距/主轴排列方式/交叉轴对齐方式 Column({}) {Column() {}.width(80%).height(50).backgroundColor(0xF5DEB3)Column() {}.width(80%).height(50).backgroundColor(0xD2B48C)Column() {}.width(80%).height(50).backgroundColor(0xF5DEB3) } .width(1…

vue3通过v-model实现父子组件通信

单一值传递 父组件 <template><div ><h1>v-model实现父子组件通讯</h1><hr><child1 v-model"num"></child1><!-- 上下两个是等价的 --><child1 :modelValue"num" update:modelValue"handle&quo…

软件测试项目经验简历包装怎么写?

软件测试是使用人工或者自动的手段来运行或者测定某个软件系统的过程&#xff0c;其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别。 在软件投入使用前&#xff0c;要经过一系列的严格测试&#xff0c;才能保证交付质量。 一、引言 1.编写目的 本文档…

有趣的代码——猜数字游戏的实现

前面介绍过很多的C语言常识&#xff0c;但是我们都知道“兴趣是最好的老师”&#xff0c;所以&#xff0c;今天我们用之前讲过的一些知识&#xff0c;加上部分新补充的知识点&#xff0c;写一个“猜数字”的小游戏&#xff0c;来丰富我们的编程学习生活&#xff0c;感受来自C语…

【小布_ORACLE】Part11-1--RMAN Backups笔记

Oracle的数据备份于恢复RMAN Backups 学习第11章需要掌握&#xff1a; 一.RMAN的备份类型 二.使用backup命令创建备份集 三.创建备份文件 四.备份归档日志文件 五.使用RMAN的copy命令创建镜像拷贝 文章目录 Oracle的数据备份于恢复RMAN Backups1.RMAN Backup Concepts&#x…

云原生系列Go语言篇-编写测试Part 1

本文来自正在规划的​​Go语言&云原生自我提升系列​​&#xff0c;欢迎关注后续文章。 2000年以来&#xff0c;自动化测试的广泛应用可能比任何其他软件工程技术都更能提高代码质量。Go是一种专注于提高软件质量的语言和生态系统&#xff0c;很自然的在其标准库中包含了测…

《2023全球隐私计算报告》正式发布!

2023全球隐私计算报告 1、2023全球隐私计算图谱2、国内外隐私计算相关政策3、隐私计算技术的最新发展4、隐私计算技术的合规挑战5、隐私计算的应用市场动态6、隐私计算开源整体趋势7、隐私计算的未来趋势 11月23日&#xff0c;由浙江省人民政府、商务部共同主办&#xff0c;杭州…

Appium自动化如果出现报错怎么办?这么做确实解决问题

解决通过appium的inspector功能无法启动app的原因 在打开appium-desktop程序&#xff0c;点击inspector功能&#xff0c;填写app的配置信息&#xff0c;启动服务提示如下&#xff1a; 报错信息&#xff1a; An unknown server-side error occurred while processing the com…

牛客 算法题 记负均正II golang实现

题目 HJ105 记负均正II golang 实现 package mainimport ("bufio""fmt""io""os""strconv""strings" )func main() {scanner : bufio.NewScanner(os.Stdin)nums:make([]int,0)sum:0minus:0for scanner.Scan() {l…

java开发需要掌握的maven相关知识和Junit单元测试

maven简介 什么是maven&#xff1a; maven是一款管理和构建java项目的工具&#xff0c;是apache旗下的一个开源项目。 maven的作用&#xff1a; 依赖管理&#xff1a; 方便快捷的管理项目依赖的资源&#xff08;jar包&#xff09;。 项目构建&#xff1a; 标准化的跨平台&#…

20 章 多线程

20.1线程简介. 20.2创建线程 2.1继承Thread类 Thread 类是java.lang包中的一个类&#xff0c;从这个类中实例化的对象代表线程&#xff0c;程序员启动一个新线程需要建立Thread 实例。Thread类中常用的两个构造方法如下: public Thread():创建一个新的线程对象。 public Thre…

【LeetCode】128. 最长连续序列——哈希的应用(3)

文章目录 1、思路2、解题方法3、复杂度时间复杂度:空间复杂度: 4、Code Problem: 128. 最长连续序列 1、思路 我会用一种做题者的思路来去看待这道题。 我们在乍一看到这道题的时候&#xff0c;看到它的时间复杂度要求为O(N)&#xff0c;然后又要求去找序列(就是让你判断这个…

stm32 TIM

一、TIM简介 TIM&#xff08;Timer&#xff09;定时器定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断。16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时定时器不仅具备基本的定时中断功能&…

前端 | iframe框架标签应用

文章目录 &#x1f4da;嵌入方式&#x1f4da;图表加载显示&#x1f4da;100%嵌入及滑动条问题&#x1f4da;加载动画保留 前情提要&#xff1a; 计划用iframe把画好的home1.html&#xff08;echarts各种图表组成的html数据大屏&#xff09;嵌入整合到index.html&#xff08;搭…

只需十分钟快速入门Python,快速了解基础内容学习。零基础小白入门适用。

文章目录 简介特点搭建开发环境版本hello world注释文件类型变量常量数据类型运算符和表达式控制语句数组相关函数相关字符串相关文件处理对象和类连接mysql关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源…

摇滚史密斯2014重置版外接声卡

摇滚史密斯2014重置版外接声卡 前提 由于rs_asio是通过模拟原厂线的方法来使游戏可以支持声卡的&#xff0c;因此&#xff0c;声卡的采样频率需要满足原厂线要求&#xff0c;即采样率可以设置为 48000 Hz。 我使用的是 Sonic Cube 这款声卡&#xff0c;非常幸运&#xff0c;…