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 中,你可以使用 setTimeout 和 setInterval 函数来实现定时任务。
这两个函数都接受一个回调函数作为参数,并在指定的时间后执行该回调函数。
以下是使用 setTimeout 和 setInterval 实现定时任务的示例代码:
// 使用 setTimeout 实现定时任务
setTimeout(() => {console.log('定时任务执行');
}, 1000); // 1000 毫秒后执行回调函数// 使用 setInterval 实现定时任务
setInterval(() => {console.log('每隔 1 秒执行定时任务');
}, 1000); // 每隔 1 秒执行回调函数
在上面的示例中,我们使用 setTimeout 函数设置了一个定时任务,它将在 1 秒后执行。然后,我们使用 setInterval 函数设置了一个每隔 1 秒执行一次的定时任务。
请注意,setTimeout 和 setInterval 函数返回一个定时器标识符,你可以使用这个标识符来取消定时任务。例如:
let timerId = setTimeout(() => {console.log('定时任务执行');
}, 1000);clearTimeout(timerId); // 取消定时任务
在上面的示例中,我们使用 clearTimeout 函数取消了之前设置的定时任务。同样,你也可以使用 clearInterval 函数来取消 setInterval 设置的定时任务。
解释定时器和延时器在事件循环中的工作原理
定时器(
setTimeout和setInterval)和延时器(setImmediate和setTimeout函数的第二个参数)在事件循环中以不同的方式工作。
-
setTimeout和setInterval:这两个函数将回调函数添加到定时器事件队列(timers)中。当事件循环到达timers阶段时,它会按照到期时间的顺序执行回调函数。setTimeout函数接受两个参数:回调函数和延迟时间(以毫秒为单位)。它将回调函数添加到定时器事件队列中,并在指定的延迟时间后执行。setInterval函数与setTimeout类似,除了它会在每次回调函数执行后重新将其添加到定时器事件队列中,以实现周期性执行。
-
setImmediate:这个函数将回调函数添加到setImmediate队列中。setImmediate队列中的回调函数会在事件循环的check阶段被执行,并且在timers队列之前。-
与
setTimeout不同,setImmediate的回调函数会在当前事件循环迭代中尽快执行,而不是在特定的延迟时间后执行。 -
由于
setImmediate队列在timers队列之前执行,因此如果同时使用setTimeout和setImmediate,setImmediate队列中的回调函数会先执行。
-
需要注意的是,setImmediate 并不是在所有环境中都被支持,它是 Node.js 中的一个扩展,而在浏览器环境中不被支持。在浏览器中,可以使用 Promise 的 then 方法或 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 可以在一定程度上优化事件循环的性能。
以下是一些建议:

-
使用 Promise.all:如果你有多个异步操作需要同时完成,可以使用
Promise.all方法。它接受一个 Promise 对象数组作为参数,并返回一个新的 Promise 对象,该对象在所有传入的 Promise 对象都完成后才会完成。这样可以避免多个异步操作的回调函数在事件循环中被多次处理,提高性能。 -
使用 Async/Await: Async/Await 是基于 Promise 的语法糖,它使得异步代码的编写更加简洁和直观。使用 Async/Await 可以避免回调地狱的问题,并使代码更易于阅读和维护。在使用 Async/Await 时,注意将异步操作放在
async函数中,并使用await关键字等待 Promise 的完成。 -
避免嵌套的异步操作:过多的嵌套异步操作可能会导致事件循环的性能下降。尽量保持异步操作的层次结构简单,并避免在回调函数中再次进行异步操作。如果需要处理嵌套的异步操作,可以使用
Promise的链式调用或使用async/await来简化代码。 -
使用微任务:在 Async/Await 中,遇到
await关键字时,JavaScript 引擎会将异步函数的执行挂起,并将其添加到微任务队列中。微任务队列会在事件循环的microtask阶段被处理。利用微任务队列可以实现一些性能优化,例如在微任务队列中进行一些轻量级的操作,以避免阻塞事件循环。 -
合理使用
Promise的 resolve 和 reject:在创建Promise对象时,尽量避免在Promise的构造函数中传入立即 resolve 或 reject 的值。这可能会导致不必要的微任务被添加到微任务队列中,从而影响性能。尽量在需要时才 resolve 或 rejectPromise对象。
通过合理使用 Promise 和 Async/Await,可以使事件循环的性能得到一定程度的优化。但具体的优化效果还需要根据实际情况进行评估和测试,以确保在你的应用中获得最佳性能。
总结与展望
总结 Node.js 事件循环的核心概念和工作原理
Node.js 事件循环的核心概念和工作原理可以总结如下:
-
事件循环:Node.js 是基于事件驱动的非阻塞 I/O 模型。事件循环是 Node.js 处理事件的核心机制,它负责监听和处理各种事件,并在需要时执行相应的回调函数。
-
事件队列:事件循环使用事件队列来管理事件。有不同类型的事件队列,包括定时器事件队列、I/O 事件队列和
setImmediate队列。这些队列按照特定的顺序进行处理。 -
阶段:事件循环分为多个阶段,包括
timers阶段、I/O阶段和poll阶段等。每个阶段会处理特定类型的事件。 -
回调函数:当事件发生时,相应的回调函数会被添加到事件队列中。事件循环会在适当的阶段取出队列中的回调函数并执行它们。
-
非阻塞 I/O:Node.js 使用非阻塞 I/O 模型,这意味着在执行 I/O 操作时,事件循环不会被阻塞。相反,它会将 I/O 操作委托给操作系统,并在 I/O 操作完成后触发相应的事件。
-
异步编程:由于 Node.js 的非阻塞特性,异步编程成为常见的编程方式。使用异步函数和回调函数可以在不阻塞事件循环的情况下执行异步操作。
-
定时器和延时器:定时器和延时器是在事件循环中管理时间相关操作的重要工具。定时器会在指定的时间后将回调函数添加到定时器事件队列中,而延时器会在当前事件循环迭代结束后将回调函数添加到下一个事件循环迭代的
poll阶段。
通过事件循环,Node.js 能够高效地处理大量的 I/O 操作和并发请求,提供了一种高效的异步编程模型。了解事件循环的工作原理对于编写高效和可靠的 Node.js 应用至关重要。
相关文章:
Node.js 事件循环:定时任务、延迟任务和 I/O 事件的艺术
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...
陪诊系统:基于自然语言处理的患者沟通创新
医疗领域的数字化转型正日益引入创新技术,其中基于自然语言处理(NLP)的陪诊系统成为提升患者沟通的一项关键技术。本文将深入研究这一领域,介绍陪诊系统如何借助NLP实现患者沟通的创新,并提供一个简单的Python代码示例…...
实用攻略——SD-WAN网络配置步骤详解
SD-WAN(软件定义广域网)作为一种新兴的网络技术,被广泛应用于构建高效、可靠的企业组网。 本文将详细介绍企业组网中SD-WAN涉及的配置过程,并提供一些配置技巧,以帮助企业快速了解企业组网的配置。通过使用SD-WAN技术&…...
无人机摄影测量
无人机摄影测量技术是传统航空摄影测量手段的有力补充,具有机动灵活、高效快速、精细准确、作业成本低、生产周期短、影像获取空间分辨率高、高危地区探测等优势。无人机与航空摄影测量相结合使得“无人机数字低空遥感”成为航空遥感领域的一个崭新发展方向。无人机…...
对el-select封装成组件使用
效果与直接使用el-select一样,多处用el-select显得代码冗余就进行了封装 效果图: el-select封装: <template><div class"my-select"><el-selectv-model"person.modelValue":placeholder"placehold…...
pytorch 多卡并行训练
目录 设置参数: 训练时参数: 调用命令: 设置参数: import argparseparser argparse.ArgumentParser()parser.add_argument(--batch_size, typeint, default64, helpBatch size for training)parser.add_argument(--local_rank…...
C# Bin、XML、Json的序列化和反序列化
1)序列化前的准备 声明类: [Serializable]public class BandItem{//JsonIgnore:当不想把某字段值序列化到Json时使用//[JsonIgnore]public string Name { get; set; }public string MusicStyle { get; set; }public string Masterpiece { ge…...
mediapipe+opencv实现保存图像中的人脸,抹去其他信息
mediapipeopencv MediaPipe本身不提供图像处理功能,它主要用于检测和跟踪人脸、手势、姿势等。如果您想要从图像中仅提取人脸主要信息并去除其他信息. # codingutf-8 """project: teatAuthor:念卿 刘file: test.pydate&…...
clickhouse的向量化执行
背景 clickhouse快的很大一部分原因来源于数据的向量化执行,本文就来看一下向量化执行和正常标量执行的区别 SIMD的向量化执行 从上图可知,clickhouse通过SIMD指令可以做到一个cpu周期操作两个向量的运算操作,比起普通的cpu指令效率提高了N…...
R语言实验三
1、读取一个文件并进行如下操作。 ①使用命令清空工作空间,使用read.table读取exam_1.txt文件,将文件保存到data变量中,数据第一行设置为列名,第一列是行名。 ②判断对象data是否为矩阵。 ③将对象转换为矩阵,记为d…...
springboot-mongodb-连接配置
文章目录 配置Maven依赖URL格式单节点配置示例副本集(含连接池配置) 配置Maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependenc…...
基于单片机的多功能视力保护器(论文+源码)
1.系统设计 多功能视力保护器在设计过程中能够对用户阅读过程中的各项数据信息进行控制,整体设计分为亮种模式,分别是自动模式,手动模式。在自动模式的控制下,当单片机检测当前光照不强且有人时就开启LED灯,并且会根据…...
如何保护 API 安全
为了收集有关 API 管理当前和未来状态的见解,我们邀请来自 18 家公司的 IT 专业人士分享他们的想法。我们问他们:“哪些技术和工具对于保护 API 最有效?” 他们告诉我们的是: 验证 我们经常向已知的 B2B 合作伙伴提供 API 访问权…...
工业机器视觉megauging(向光有光)使用说明书(十五,轻量级的visionpro)
程序(软件)的一些不足和建议:(后续会跟进) 不足:(如果你发现了,谢谢及时提出来) 1,找线工具有噪点抑制功能;blob跟随工具,匹配跟随工…...
Linux e2fsck命令教程:如何检查和修复文件系统(附案例详解和注意事项)
Linux e2fsck命令介绍 e2fsck是一个用于检查Linux第二扩展文件系统(ext2fs)的命令。它也支持包含日志的ext2文件系统,这些文件系统有时也被称为ext3文件系统。该命令会自动保存找到的坏块到文件系统中,以便这些硬盘的部分不再被使…...
TypeScript 的安装与使用
npm i typescript -g依赖会安装 tsc 命令 tsc -v初始化 配置 tsc --init生成 tsconfig.json tsconfig.json 配置 要支持 import 模块语法的话 rollup的配置 target 选项需要设置 ES5 module 选项需要改成 es2015 esbuild 的配置 target: ESNext module: ESNext moduleReso…...
Git版本管理配置说明 - Visual Studio
一、 Git服务端配置 在源代码管理服务器新建文件夹,并配置共享访问权限Everyone(读取/写入)。 在本地访问这台服务器共享目录,确保正确打开。 在VS中打开项目,点选Git更改,点击“创建Git仓库”,创建项目初始版本。 弹出如下对话框: 因为我们只是在局域网中开发项…...
Rust语言项目实战(四) - 界面绘制与渲染
回顾 前面的章节中,我们已经完成了下面的工作: 准备所需的音频文件设置游戏所需的屏幕和键盘配置创建了游戏主循环并在其中实现按键Esc或q退出游戏的功能游戏退出前还原配置 目前游戏的界面还是一片空白,本章我们开始为游戏在终端上绘制并…...
动态规划学习——回文串
目录 一,回文子串 1.题目 2.题目接口 3,解题代码及其思路 解题代码: 二, 分割回文串II 1,题目 2,题目接口 3,解题思路及其代码 一,回文子串 1.题目 给你一个字符串 s &…...
优化你的计算机性能:如何根据 CPU 占用率决定硬件升级
优化你的计算机性能:如何根据 CPU 占用率决定硬件升级 一、引言二、CPU 占用率的意义与影响三、监测和评估 CPU 占用率四、判断硬件升级需求的依据五、硬件升级方案和建议六、总结 一、引言 计算机性能优化是提升计算机系统整体效能的过程,它对于用户和…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
