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.mzph.cn/news/190179.shtml

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

相关文章

快照读通过MVCC解决不可重复读当前读通过间隙锁解决幻读

简介 Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。 *往期知识不做重点 事务具有4个特征,分别是原子性、一致性、隔…

骨传导会损伤听力吗?戴哪种耳机不伤耳朵?

使用骨传导耳机不会损伤听力,反而还能在一定程度上保护听力! 骨传导耳机最大的特点是,声波不经过外耳道和骨膜中的空气进行传播,而是直接将人体骨骼结构作为传声介质来进行传播。 在使用骨传导耳机时,骨传导耳机的声源…

Python标准库math【侯小啾python领航班系列(十六)】

Python标准库math【侯小啾python领航班系列(十六)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

SpringCloud_Gateway服务网关

文章目录 一、SpringCloudGateway服务网关概论1、SpringCloudGateway服务网关概论2、SpringCloudGateway的三大核心概念 二、SpringCloudGateway的路由及断言1、子模块项目SpringCloudGateway的搭建2、SpringCloudGateway_Java API构建路由3、SpringCloudGateway的动态路由功能…

学生备考护眼灯哪个牌子好?值得入手的护眼台灯推荐

护眼台灯作为这几年人们很关注的电器产品,家里有孩子或者是经常面对电子设备的人士,相信都会对其有所了解并且购买了护眼台灯,但是还有些家长对护眼台灯的认知不够深,以至于还没有给孩子安排上护眼台灯,还在疑惑护眼台…

FlowJo软件的简单介绍 掌控流式细胞分析的科技巨匠 FlowJo10

FlowJo 10 for Mac是一款强大的流式细胞数据分析软件,具有以下功能: 数据导入与预处理:FlowJo 10可以轻松导入各种类型的流式细胞数据,并对数据进行预处理,包括去噪、背景校正等,以确保数据的准确性和可靠…

小小窗户,在风水中有大大的作用

风水,不是迷信,是环境科学,是地磁学。过去大户人家,官府都是很注重风水的。现在,越是往社会上层越是注重风水。在实际的家居风水之中,要想生旺气场,增加财运,风水纳气一定要充足。 自…

CCC联盟数字车钥匙(七)——BLE连接流程

本文接上一篇CCC数字钥匙BLE概述,介绍BLE中相关连接流程的实现。 2、BLE流程 2.1 所有者配对连接建立 CCC中使用Bluetooth OOB(Out of Band, 带外)配对完成所有者配对、连接建立的流程。BLE设置分为以下两个子部分: BLE链路层连…

UI自动化Selenium find_elements和find_element的区别

# 如果获取的element是list,那么需要用find_elements方法;此方法会返回list,然后使用len() 方法,计算对象的个数; # find_element方法返回的不是list对象,所以导致没办法计算对象个数 # 1.返回值类型不同…

oops-framework框架 之 初始了解(一)

引擎:CocosCreator 环境: Mac Gitee: oops-framework 简介 oops-framework是由作者dgflash编写,基于CocosCreator 3.x而实现的开源框架。 该框架以插件形式存在,主要目的是为了降低与项目的耦合,并且通过插件内部的…

LeetCode | 100. 相同的树

LeetCode | 100. 相同的树 OJ链接 判断两个节点是否等于空,两个都等于空就直接返回true如果一个等于空,另一个不等于空,说明false然后再判断两个树的值是否相等最后递归p的左,q的左,p的右,q的右 bool isS…

仿真的整体框架和类图设计

之前的写的模拟代码没有模块,没有对象,写的逻辑结构也很混乱。我花了些时间进行整理,首先所有的类如下图 在管理类中有统一的管理类的接口 ,提供所有管理类的虚拟初始化和关闭方法 然后事件的管理类 我希望在这个类中管理所有的脉…

K7系列FPGA多重启动(Multiboot)

Xilinx 家的 FPGA 支持多重启动功能(Multiboot),即可以从多个 bin 文件中进行选择性加载,从而实现对系统的动态更新,或系统功能的动态调整。 这一过程可以通过嵌入在 bit 文件里的 IPROG 命令实现上电后的自动加载。而…

牛客剑指offer刷题模拟篇

文章目录 顺时针打印矩阵题目思路代码实现 扑克牌顺子题目思路代码实现 把字符串转换成整数题目思路代码实现 表示数值的字符串题目思路代码实现 顺时针打印矩阵 题目 描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如&#xf…

机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割模型以及TensorRT部署说明

一、简述 虽然Segment Anything用于图像分割的通用大模型看起来很酷(飞桨也提供分割一切的模型),但是个人感觉落地应用的时候心里还是更倾向于飞桨这种场景式的,因为需要用到一些人体分割的需求,所以这里主要是对飞桨高性能图像分割开发套件进行了解和使用,但是暂时不训练…

Java 线程同步和通信

Android 11 废弃了AsyncTask 线程 Thread: 通过start 开启 源码: start0 native方法 通过虚拟机跟操作系统交互 进程和线程区别: 进程是操作系统的独立区域,各个区域互不干扰,一个进程可以有多条线程同时工作,进程大于线程,线程依赖进程,线程间可以共享资源 Runnable: 接口…

matlab操作方法(二)——基本作图

matlab提供很多灵活的二维作图功能函数。这些作图函数分为3类:图形处理、曲线和曲面图的创建、注释和图形特性。作图函数虽多,但语法大致相同 在 MATLAB 中,figure 函数用于创建或选择图形窗口。 matlab figure函数的用法_matlab中figure-C…

89基于matlab的人工蜂群和粒子群混合优化的路径规划算法

基于matlab的人工蜂群和粒子群混合优化的路径规划算法,起点和终点确定的前提下,在障碍物中寻找最佳路径。数据可更换自己的,程序已调通,可直接运行。 89人工蜂群和粒子群混合优化 (xiaohongshu.com)https://www.xiaohongshu.com/e…

用JavaScript的管道方法简化代码复杂性

用JavaScript的管道方法简化代码复杂性 在现代 web 开发中,维护干净有效的代码是必不可少的。随着项目的增加,我们功能的复杂性也在增加。然而,javaScript为我们提供了一个强大的工具,可以将这些复杂的函数分解为更小的、可管理的…

【 RTTI 】

RTTI 概念: RTTI(Run Time Type Identification)即通过运行时类型识别,程序能够使用基类的指针或引用来检 查着这些指针或引用所指的对象的实际派生类型。 原因: C是一种静态类 型语言。其数据类型是在编译期就确定的,不能在运…