当前位置: 首页 > news >正文

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 中,事件循环处理不同的阶段,每个阶段负责执行某些类型的回调。它帮助管理非阻塞任务,确保函数可以异步执行。在这些阶段中,有不同的队列。对于本次讨论,有两个队列是重要的:

  • 宏任务队列:这是 setTimeoutsetImmediate 等任务所在的地方。
  • 微任务队列:这是 promises (Promise.then()) 和 process.nextTick() 回调所在的地方。

事件循环的工作原理

要理解 setTimeout()setImmediate() 的工作原理,我们需要看看 Node.js 中的事件循环。事件循环允许 Node.js 处理异步代码。它在不同的阶段处理不同类型的操作,每个阶段负责特定的任务。

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │└───────────────────────────┘
  1. 定时器阶段:这是处理 setTimeout() 回调的地方。即使是 0 毫秒的延迟,它们也要等到下一次循环迭代才能执行。
  2. 待处理回调阶段:处理已完成的 I/O 事件,但我们的示例中没有,所以跳过这个阶段。
  3. 检查阶段setImmediate() 回调在这里运行。它们在 I/O 任务之后立即执行,但在 setTimeout() 回调之前。
  4. 轮询阶段:处理新的传入 I/O 操作,如文件读取或网络请求。如果没有 I/O,事件循环会跳过这个阶段。
  5. 下一次循环迭代:在检查阶段之后,事件循环回到处理下一个定时器阶段,在那里 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 回调会一起运行?

  1. 相同的事件循环周期:两个 setImmediate 调用在事件循环的同一个周期(或循环)中被放置到宏任务队列中。Node.js 按顺序处理这些任务。
  2. 优先于 setTimeout():即使 setTimeout() 设定了 0 延迟,这也不保证立即执行。setImmediate() 回调在当前周期中优先于 setTimeout() 任务。

现实世界的类比

想象一下在餐馆点餐和饮料。

  1. 你点了一道菜(代表 setTimeout(0))。
  2. 厨师将其添加到订单队列中,一旦准备好就会送达。
  3. 同时,你要了一杯水(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 中&#xff0c;setImmediate() 和 setTimeout() 都用于调度任务&#xff0c;但它们的工作方式不同。 JavaScript 的异步特性 JavaScript 以其非阻塞、异步行为而闻名&#xff0c;尤其是在 Node.js 环境…...

【Java文件操作】文件系统操作文件内容操作

文件系统操作 常见API 在Java中&#xff0c;File类是用于文件和目录路径名的抽象表示。以下是一些常见的方法&#xff1a; 构造方法&#xff1a; File(String pathname)&#xff1a;根据给定的路径创建一个File对象。File(String parent, String child)&#xff1a;根据父路径…...

关于若依flowable的安装

有个项目要使用工作流功能&#xff0c;在网上看了flowable的各种资料&#xff0c;最后选择用若依RuoYi-Vue-Flowable这个项目来迁移整合。 一、下载项目代码&#xff1a; 官方项目地址&#xff1a;https://gitee.com/shenzhanwang/Ruoyi-flowable/ 二、新建数据库&#xff…...

猜数字困难版(1-10000)

小游戏&#xff0c;通过提示每次猜高或猜低以及每次猜中的位数&#xff0c;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应用参数是包含了在系统或软件层级可被更改的数据的软件变量&#xff0c;他们影响系统或软件的行为和属性。应用参数的概念有两种表达方式:规范(分别包括变量名称、值域范围、…...

Knife4j:打造优雅的SpringBoot API文档

1. 为什么需要API文档&#xff1f; 在现代软件开发中,API文档的重要性不言而喻。一份清晰、准确、易于理解的API文档不仅能够提高开发效率,还能降低前后端沟通成本。今天,我们要介绍的Knife4j正是这样一款强大的API文档生成工具,它专为Spring Boot项目量身打造,让API文档的生成…...

数学建模笔记—— 多目标规划

数学建模笔记—— 多目标规划 多目标规划1. 模型原理1.1 多目标规划的一般形式1.2 多目标规划的解1.3 多目标规划的求解 2. 典型例题3. matlab代码实现 多目标规划 多目标规划是数学规划的一个分支。研究多于一个的目标函数在给定区域上的最优化。又称多目标最优化。通常记为 …...

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数 一、环境说明二、页面之间相互传参 一、环境说明 DevEco Studio 版本&#xff1a; API版本&#xff1a;以12为主 二、页面之间相互传参 说明&#xff1a; 页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页…...

SonicWall SSL VPN曝出高危漏洞,可能导致防火墙崩溃

近日&#xff0c;有黑客利用 SonicWall SonicOS 防火墙设备中的一个关键安全漏洞入侵受害者的网络。 这个不当访问控制漏洞被追踪为 CVE-2024-40766&#xff0c;影响到第 5 代、第 6 代和第 7 代防火墙。SonicWall于8月22日对其进行了修补&#xff0c;并警告称其只影响防火墙的…...

关于SAP标准委外(带料外协)采购订单信息

业务背景&#xff1a; 业务部门提出需要将售料外协方式变更为带料外协&#xff0c;带料外协实际业务存在一个委外订单存在多次发料&#xff0c;且每次发票需要进行齐套发料&#xff0c;不同批次的发料涉及物料替代。在半成品收货时需要进行对发料的组件进行扣料。 需求分析&a…...

SpringBoot整合WebSocket实现消息推送或聊天功能示例

最近在做一个功能&#xff0c;就是需要实时给用户推送消息&#xff0c;所以就需要用到 websocket springboot 接入 websocket 非常简单&#xff0c;只需要下面几个配置即可 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) 中另一个重要的财务管理模块&#xff0c;主要用于管理企业销售过程中的账款回收。下面是AR模块中的一些关键财务流程及其详细说明&#xff1a; 1. 销售订单管理 创建销售订单&#xff1a;当客户下单时&#xff0c;销售人员…...

Minitab 的直方图结果分析解释

Minitab 的直方图结果分析解释 步骤 1&#xff1a;评估关键特征 检查分布的尖峰和散布。评估样本数量对直方图外观的影响。 标识尖峰&#xff08;即&#xff0c;条的最高聚类&#xff09;&#xff1a; 尖峰表示样本中最常见的值。评估样本的散布以了解数据的变异程度。例如…...

AgentRE:用智能体框架提升知识图谱构建效果,重点是开源!

发布时间&#xff1a;2024 年 09 月 13 日 Agent应用 AgentRE: An Agent-Based Framework for Navigating Complex Information Landscapes in Relation Extraction 在复杂场景中&#xff0c;关系抽取 (RE) 因关系类型多样和实体间关系模糊而挑战重重&#xff0c;影响了传统 “…...

力扣题解2390

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述​&#xff08;中等&#xff09;&#xff1a; 从字符串中移除星号 给你一个包含若干星号 * 的字符串 s 。 在一步操作中&#xff0c;你可以&#xff1a; 选中 s 中的一个星号。 移除星号…...

用Python获取PDF页面的大小、方向和旋转角度

在文档管理和自动化领域&#xff0c;了解PDF文档的内在属性&#xff08;如页面大小、方向和旋转角度&#xff09;对于确保一致的文档处理和布局保真度至关重要。这些属性在内容重用、归档以及PDF无缝集成到网络环境或其他数字工作流程中起着关键作用&#xff0c;因为它们直接影…...

【即时通讯】轮询方式实现

技术栈 LayUI、jQuery实现前端效果。django4.2、django-ninja实现后端接口。 代码仓 - 后端 代码仓 - 前端 实现功能 首次访问页面并发送消息时需要设置昵称发送内容为空时要提示用户不能发送空消息前端定时获取消息&#xff0c;然后展示在页面上。 效果展示 首次发送需要…...

Flock 明牌空投教程

FLock 旨在为人工智能构建一个去中心化的隐私保护解决方案。FLock提出了一项名为联合学习区块&#xff08;简称 FLocks&#xff09;的研究计划&#xff0c;该计划使用区块链作为数据持有者之间的协调平台来进行机器学习&#xff0c;同时数据保持本地和隐私。通过用区块链取代收…...

项目内部调用的远程接口开发

编写一个项目内部调用的远程接口通常是为了在分布式系统或者微服务架构中&#xff0c;实现各个服务之间的通信和数据交换。这样的远程接口专门用于服务之间的调用&#xff0c;而不是直接暴露给外部用户或前端。 项目内部的远程接口统一放在api工程 首先进入api编写接口&#x…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...