setImmediate() vs setTimeout() 在 JavaScript 中的区别
setImmediate() vs setTimeout() 在 JavaScript 中的区别
在 JavaScript 中,setImmediate() 和 setTimeout() 都用于调度任务,但它们的工作方式不同。
JavaScript 的异步特性
JavaScript 以其非阻塞、异步行为而闻名,尤其是在 Node.js 环境中。如果你曾经参与过涉及定时器或回调的项目,你可能遇到过 setTimeout(),甚至 setImmediate()。乍一看,这两个函数似乎做的是同一件事——调度任务以便稍后运行。但如果你曾经一起运行它们,你可能会注意到一些有趣的行为。
尽管它们的目的相似,但 setImmediate() 和 setTimeout() 在底层的操作方式不同。如果你想知道为什么 setImmediate() 回调似乎一个接一个地运行,而 setTimeout() 回调则是间隔开的,本指南将为你解析其中的原因。
这不仅仅是 JavaScript 的一个怪癖;它与 Node.js 如何管理异步任务密切相关。理解这两个函数之间的差异将帮助你更好地控制代码的时间和执行顺序,这对于大型应用程序尤其重要,因为即使是时间上的微小失误也可能导致难以发现的错误。
我们将深入探讨事件循环,它如何处理这些定时器,以及为什么在一起使用它们时事情并不总是按预期发生。到最后,你将更清楚地了解何时使用 setTimeout() 或 setImmediate(),以满足你所需的时间行为。
行为差异
setImmediate(() => {console.log("setImmediate 1");
});setTimeout(() => {console.log("setTimeout 1");
}, 0);setTimeout(() => {console.log("setTimeout 2");
}, 0);setImmediate(() => {console.log("setImmediate 2");
});
当你运行这段代码时,你可能期望 setTimeout 回调按定义的顺序执行,然后是 setImmediate 回调。但你在控制台中看到的是:
setTimeout 1
setImmediate 1
setImmediate 2
setTimeout 2
如果这让你感到困惑,不要担心。让我们解开其中的原因。
事件循环
要理解这一点,我们需要快速了解 Node.js 如何管理异步操作。Node.js 的异步特性核心是事件循环。
在 Node.js 中,事件循环处理不同的阶段,每个阶段负责执行某些类型的回调。它帮助管理非阻塞任务,确保函数可以异步执行。在这些阶段中,有不同的队列。对于本次讨论,有两个队列是重要的:
- 宏任务队列:这是
setTimeout和setImmediate等任务所在的地方。 - 微任务队列:这是 promises (
Promise.then()) 和process.nextTick()回调所在的地方。
事件循环的工作原理
要理解 setTimeout() 和 setImmediate() 的工作原理,我们需要看看 Node.js 中的事件循环。事件循环允许 Node.js 处理异步代码。它在不同的阶段处理不同类型的操作,每个阶段负责特定的任务。
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │└───────────────────────────┘
- 定时器阶段:这是处理
setTimeout()回调的地方。即使是 0 毫秒的延迟,它们也要等到下一次循环迭代才能执行。 - 待处理回调阶段:处理已完成的 I/O 事件,但我们的示例中没有,所以跳过这个阶段。
- 检查阶段:
setImmediate()回调在这里运行。它们在 I/O 任务之后立即执行,但在setTimeout()回调之前。 - 轮询阶段:处理新的传入 I/O 操作,如文件读取或网络请求。如果没有 I/O,事件循环会跳过这个阶段。
- 下一次循环迭代:在检查阶段之后,事件循环回到处理下一个定时器阶段,在那里
setTimeout()回调最终运行。
setTimeout() 的 0 延迟
当你使用 setTimeout() 并设置延迟为 0 时,你实际上是在告诉 Node.js 在当前操作完成后尽快运行回调。然而,重要的是要记住,“尽快”仍然取决于事件循环的阶段。
setTimeout(() => {console.log("setTimeout 1 with 0 delay");
}, 0);setImmediate(() => {console.log("setImmediate 1");
});setTimeout(() => {console.log("setTimeout 2 with 0 delay");
}, 0);
输出结果:
setTimeout 1 with 0 delay
setImmediate 1
setTimeout 2 with 0 delay
即使延迟为 0,setTimeout() 回调仍然需要等待定时器阶段的下一次循环,因此不会立即运行。相反,它被放置在宏任务队列中,以便在下一个可用机会执行。
setImmediate()
另一方面,setImmediate() 设计用于在 I/O 事件完成后执行回调,在同一事件循环迭代中。这意味着 setImmediate() 回调在额外的定时器(如 setTimeout())执行之前被处理,特别是在没有 I/O 的情况下。
在我们的示例中,由于没有 I/O 发生,两个 setImmediate() 回调会一个接一个地执行,然后才轮到第二个 setTimeout() 回调。
为什么 setImmediate 回调会一起运行?
- 相同的事件循环周期:两个
setImmediate调用在事件循环的同一个周期(或循环)中被放置到宏任务队列中。Node.js 按顺序处理这些任务。 - 优先于
setTimeout():即使setTimeout()设定了 0 延迟,这也不保证立即执行。setImmediate()回调在当前周期中优先于setTimeout()任务。
现实世界的类比
想象一下在餐馆点餐和饮料。
- 你点了一道菜(代表
setTimeout(0))。 - 厨师将其添加到订单队列中,一旦准备好就会送达。
- 同时,你要了一杯水(
setImmediate()),由于它快速且容易准备,服务员会在你的食物完成之前立即送达。
在这个类比中,水(快速任务)首先被处理,即使两个订单几乎同时下达。菜(稍微复杂一些)稍后送达。
这种情况总是发生吗?
不一定。setImmediate() 和 setTimeout() 的行为可能取决于代码中发生的其他异步操作。如果有 I/O 操作,执行顺序可能会改变,因为 setImmediate() 只会在 I/O 事件完成后运行。
const fs = require("fs");fs.readFile("example.txt", () => {setTimeout(() => {console.log("setTimeout after I/O");}, 0);setImmediate(() => {console.log("setImmediate after I/O");});
});
输出结果:
setImmediate after I/O
setTimeout after I/O
在这种情况下,setImmediate() 总是在 setTimeout() 之前运行,因为事件循环在 I/O 回调之后优先处理 setImmediate()。
当没有 I/O 事件时,两个 setImmediate() 回调会一个接一个地运行,然后才轮到 setTimeout() 回调。
process.nextTick() 和 Promises
以下示例展示了 Node.js 中各种异步操作的处理方式:
setTimeout(() => {console.log("setTimeout");
}, 0);setImmediate(() => {console.log("setImmediate");
});Promise.resolve().then(() => {console.log("Promise then");
});process.nextTick(() => {console.log("process.nextTick");
});
输出结果:
process.nextTick
Promise then
setTimeout
setImmediate
process.nextTick():这将在任何其他任务之前运行,甚至在微任务(如 Promises)之前。Promise.then():这是一个微任务,因此它在当前操作之后但在宏任务(如setTimeout()和setImmediate())之前运行。setTimeout():在微任务处理完之后运行。setImmediate():尽管它类似于setTimeout(),但它在事件循环周期的后期运行,在当前 I/O 操作之后。
Node.js 的异步行为有时可能会令人困惑,特别是在处理 setTimeout() 和 setImmediate() 时。关键是理解事件循环以及任务在不同阶段的调度方式。
setImmediate()在 I/O 事件之后和当前事件循环周期内运行。setTimeout()在指定的延迟之后运行,即使延迟为 0,它也会为下一次事件循环迭代调度任务。- 当没有 I/O 操作时,
setImmediate()会在下一个setTimeout()之前连续执行。
理解这些差异有助于你精确控制代码的运行时间,这在高性能应用程序中至关重要,因为时间和效率非常重要。
相关文章:
setImmediate() vs setTimeout() 在 JavaScript 中的区别
setImmediate() vs setTimeout() 在 JavaScript 中的区别 在 JavaScript 中,setImmediate() 和 setTimeout() 都用于调度任务,但它们的工作方式不同。 JavaScript 的异步特性 JavaScript 以其非阻塞、异步行为而闻名,尤其是在 Node.js 环境…...
【Java文件操作】文件系统操作文件内容操作
文件系统操作 常见API 在Java中,File类是用于文件和目录路径名的抽象表示。以下是一些常见的方法: 构造方法: File(String pathname):根据给定的路径创建一个File对象。File(String parent, String child):根据父路径…...
关于若依flowable的安装
有个项目要使用工作流功能,在网上看了flowable的各种资料,最后选择用若依RuoYi-Vue-Flowable这个项目来迁移整合。 一、下载项目代码: 官方项目地址:https://gitee.com/shenzhanwang/Ruoyi-flowable/ 二、新建数据库ÿ…...
猜数字困难版(1-10000)
小游戏,通过提示每次猜高或猜低以及每次猜中的位数,10次内猜中1-10000的一个数。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthde…...
ASPICE术语表
术语来源描述活动Automotive SPICE V4.0由利益相关方或参与方执行的任务用参数Automotive SPICE V4.0应用参数是包含了在系统或软件层级可被更改的数据的软件变量,他们影响系统或软件的行为和属性。应用参数的概念有两种表达方式:规范(分别包括变量名称、值域范围、…...
Knife4j:打造优雅的SpringBoot API文档
1. 为什么需要API文档? 在现代软件开发中,API文档的重要性不言而喻。一份清晰、准确、易于理解的API文档不仅能够提高开发效率,还能降低前后端沟通成本。今天,我们要介绍的Knife4j正是这样一款强大的API文档生成工具,它专为Spring Boot项目量身打造,让API文档的生成…...
数学建模笔记—— 多目标规划
数学建模笔记—— 多目标规划 多目标规划1. 模型原理1.1 多目标规划的一般形式1.2 多目标规划的解1.3 多目标规划的求解 2. 典型例题3. matlab代码实现 多目标规划 多目标规划是数学规划的一个分支。研究多于一个的目标函数在给定区域上的最优化。又称多目标最优化。通常记为 …...
【鸿蒙HarmonyOS NEXT】页面之间相互传递参数
【鸿蒙HarmonyOS NEXT】页面之间相互传递参数 一、环境说明二、页面之间相互传参 一、环境说明 DevEco Studio 版本: API版本:以12为主 二、页面之间相互传参 说明: 页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页…...
SonicWall SSL VPN曝出高危漏洞,可能导致防火墙崩溃
近日,有黑客利用 SonicWall SonicOS 防火墙设备中的一个关键安全漏洞入侵受害者的网络。 这个不当访问控制漏洞被追踪为 CVE-2024-40766,影响到第 5 代、第 6 代和第 7 代防火墙。SonicWall于8月22日对其进行了修补,并警告称其只影响防火墙的…...
关于SAP标准委外(带料外协)采购订单信息
业务背景: 业务部门提出需要将售料外协方式变更为带料外协,带料外协实际业务存在一个委外订单存在多次发料,且每次发票需要进行齐套发料,不同批次的发料涉及物料替代。在半成品收货时需要进行对发料的组件进行扣料。 需求分析&a…...
SpringBoot整合WebSocket实现消息推送或聊天功能示例
最近在做一个功能,就是需要实时给用户推送消息,所以就需要用到 websocket springboot 接入 websocket 非常简单,只需要下面几个配置即可 pom 文件 <!-- spring-boot-web启动器 --><dependency><groupId>org.springframewo…...
使用 QEMU 模拟器运行 FreeRTOS 实时操作系统
文章目录 QEMU 官网QEMU 文档QEMU 简介QEMU 安装QEMU 命令启动虚拟机串口控制台监控命令行 FreeRTOS安装编译工具FreeRTOS 源码RISC-V-Qemu-virt_GCC 示例编译 RISC-V-Qemu-virt_GCC启动虚拟机运行 FreeRTOS QEMU 官网 https://www.qemu.org/ QEMU 文档 https://www.qemu.or…...
Oracle EBS中AR模块的财务流程概览
应收账款 (AR) 模块是Oracle E-Business Suite (EBS) 中另一个重要的财务管理模块,主要用于管理企业销售过程中的账款回收。下面是AR模块中的一些关键财务流程及其详细说明: 1. 销售订单管理 创建销售订单:当客户下单时,销售人员…...
Minitab 的直方图结果分析解释
Minitab 的直方图结果分析解释 步骤 1:评估关键特征 检查分布的尖峰和散布。评估样本数量对直方图外观的影响。 标识尖峰(即,条的最高聚类): 尖峰表示样本中最常见的值。评估样本的散布以了解数据的变异程度。例如…...
AgentRE:用智能体框架提升知识图谱构建效果,重点是开源!
发布时间:2024 年 09 月 13 日 Agent应用 AgentRE: An Agent-Based Framework for Navigating Complex Information Landscapes in Relation Extraction 在复杂场景中,关系抽取 (RE) 因关系类型多样和实体间关系模糊而挑战重重,影响了传统 “…...
力扣题解2390
大家好,欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述(中等): 从字符串中移除星号 给你一个包含若干星号 * 的字符串 s 。 在一步操作中,你可以: 选中 s 中的一个星号。 移除星号…...
用Python获取PDF页面的大小、方向和旋转角度
在文档管理和自动化领域,了解PDF文档的内在属性(如页面大小、方向和旋转角度)对于确保一致的文档处理和布局保真度至关重要。这些属性在内容重用、归档以及PDF无缝集成到网络环境或其他数字工作流程中起着关键作用,因为它们直接影…...
【即时通讯】轮询方式实现
技术栈 LayUI、jQuery实现前端效果。django4.2、django-ninja实现后端接口。 代码仓 - 后端 代码仓 - 前端 实现功能 首次访问页面并发送消息时需要设置昵称发送内容为空时要提示用户不能发送空消息前端定时获取消息,然后展示在页面上。 效果展示 首次发送需要…...
Flock 明牌空投教程
FLock 旨在为人工智能构建一个去中心化的隐私保护解决方案。FLock提出了一项名为联合学习区块(简称 FLocks)的研究计划,该计划使用区块链作为数据持有者之间的协调平台来进行机器学习,同时数据保持本地和隐私。通过用区块链取代收…...
项目内部调用的远程接口开发
编写一个项目内部调用的远程接口通常是为了在分布式系统或者微服务架构中,实现各个服务之间的通信和数据交换。这样的远程接口专门用于服务之间的调用,而不是直接暴露给外部用户或前端。 项目内部的远程接口统一放在api工程 首先进入api编写接口&#x…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...
Qt Quick Controls模块功能及架构
Qt Quick Controls是Qt Quick的一个附加模块,提供了一套用于构建完整用户界面的UI控件。在Qt 6.0中,这个模块经历了重大重构和改进。 一、主要功能和特点 1. 架构重构 完全重写了底层架构,与Qt Quick更紧密集成 移除了对Qt Widgets的依赖&…...
