setImmediate() vs setTimeout() 在 JavaScript 中的区别

[复制链接]
发表于 2026-1-23 10:02:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
setImmediate() vs setTimeout() 在 JavaScript 中的区别

在 JavaScript 中,setImmediate() 和 setTimeout() 都用于调治使命,但它们的工作方式差异。
JavaScript 的异步特性

JavaScript 以其非壅闭、异步活动而闻名,尤其是在 Node.js 环境中。如果你曾经加入过涉及定时器或回调的项目,你大概碰到过 setTimeout(),以致 setImmediate()。乍一看,这两个函数好像做的是同一件事——调治使命以便稍后运行。但如果你曾经一起运行它们,你大概会留意到一些风趣的活动。
只管它们的目的相似,但 setImmediate() 和 setTimeout() 在底层的利用方式差异。如果你想知道为什么 setImmediate() 回调好像一个接一个地运行,而 setTimeout() 回调则是隔断开的,本指南将为你剖析此中的缘故原由。
这不但仅是 JavaScript 的一个怪癖;它与 Node.js 怎样管理异步使命密切干系。明白这两个函数之间的差异将资助你更好地控制代码的时间和实验序次,这对于大型应用步伐尤其告急,由于纵然是时间上的微小失误也大概导致难以发现的错误。
我们将深入探究变乱循环,它如那边理这些定时器,以及为什么在一起利用它们局势情并不总是按预期发生。到末了,你将更清晰地相识何时利用 setTimeout() 或 setImmediate(),以满意你所需的时间活动。
活动差异

  1. setImmediate(() => {
  2.   console.log("setImmediate 1");
  3. });
  4. setTimeout(() => {
  5.   console.log("setTimeout 1");
  6. }, 0);
  7. setTimeout(() => {
  8.   console.log("setTimeout 2");
  9. }, 0);
  10. setImmediate(() => {
  11.   console.log("setImmediate 2");
  12. });
复制代码
当你运行这段代码时,你大概盼望 setTimeout 回调按界说的序次实验,然后是 setImmediate 回调。但你在控制台中看到的是:
  1. setTimeout 1
  2. setImmediate 1
  3. setImmediate 2
  4. setTimeout 2
复制代码
如果这让你感到狐疑,不要担心。让我们解开此中的缘故原由。
变乱循环

要明白这一点,我们须要快速相识 Node.js 怎样管理异步利用。Node.js 的异步特性核心是变乱循环。
在 Node.js 中,变乱循环处理惩罚差异的阶段,每个阶段负责实验某些范例的回调。它资助管理非壅闭使命,确保函数可以异步实验。在这些阶段中,有差异的队列。对于本次讨论,有两个队列是告急的:


  • 宏使命队列:这是 setTimeout 和 setImmediate 等使命所在的地方。
  • 微使命队列:这是 promises (Promise.then()) 和 process.nextTick() 回调所在的地方。
变乱循环的工作原理

要明白 setTimeout() 和 setImmediate() 的工作原理,我们须要看看 Node.js 中的变乱循环。变乱循环允许 Node.js 处理惩罚异步代码。它在差异的阶段处理惩罚差异范例的利用,每个阶段负责特定的使命。
  1.    ┌───────────────────────────┐
  2. ┌─>│           timers          │
  3. │  └─────────────┬─────────────┘
  4. │  ┌─────────────┴─────────────┐
  5. │  │     pending callbacks     │
  6. │  └─────────────┬─────────────┘
  7. │  ┌─────────────┴─────────────┐
  8. │  │       idle, prepare       │
  9. │  └─────────────┬─────────────┘      ┌───────────────┐
  10. │  ┌─────────────┴─────────────┐      │   incoming:   │
  11. │  │           poll            │<─────┤  connections, │
  12. │  └─────────────┬─────────────┘      │   data, etc.  │
  13. │  ┌─────────────┴─────────────┐      └───────────────┘
  14. │  │           check           │
  15. │  └─────────────┬─────────────┘
  16. │  ┌─────────────┴─────────────┐
  17. └──┤      close callbacks      │
  18.    └───────────────────────────┘
复制代码

  • 定时器阶段:这是处理惩罚 setTimeout() 回调的地方。纵然是 0 毫秒的延长,它们也要比及下一次循环迭代才华实验。
  • 待处理惩罚回调阶段:处理惩罚已完成的 I/O 变乱,但我们的示例中没有,以是跳过这个阶段。
  • 查抄阶段:setImmediate() 回调在这里运行。它们在 I/O 使命之后立刻实验,但在 setTimeout() 回调之前。
  • 轮询阶段:处理惩罚新的传入 I/O 利用,如文件读取或网络哀求。如果没有 I/O,变乱循环会跳过这个阶段。
  • 下一次循环迭代:在查抄阶段之后,变乱循环回到处理惩罚下一个定时器阶段,在那边 setTimeout() 回调终极运行。
setTimeout() 的 0 延长

当你利用 setTimeout() 并设置延长为 0 时,你现实上是在告诉 Node.js 在当前利用完成后尽快运行回调。然而,告急的是要记取,“尽快”仍然取决于变乱循环的阶段。
  1. setTimeout(() => {
  2.   console.log("setTimeout 1 with 0 delay");
  3. }, 0);
  4. setImmediate(() => {
  5.   console.log("setImmediate 1");
  6. });
  7. setTimeout(() => {
  8.   console.log("setTimeout 2 with 0 delay");
  9. }, 0);
复制代码
输出效果:
  1. setTimeout 1 with 0 delay
  2. setImmediate 1
  3. 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 变乱完成后运行。
  1. const fs = require("fs");
  2. fs.readFile("example.txt", () => {
  3.   setTimeout(() => {
  4.     console.log("setTimeout after I/O");
  5.   }, 0);
  6.   setImmediate(() => {
  7.     console.log("setImmediate after I/O");
  8.   });
  9. });
复制代码
输出效果:
  1. setImmediate after I/O
  2. setTimeout after I/O
复制代码
在这种环境下,setImmediate() 总是在 setTimeout() 之前运行,由于变乱循环在 I/O 回调之后优先处理惩罚 setImmediate()。
当没有 I/O 变乱时,两个 setImmediate() 回调会一个接一个地运行,然后才轮到 setTimeout() 回调。
process.nextTick() 和 Promises

以下示例展示了 Node.js 中各种异步利用的处理惩罚方式:
  1. setTimeout(() => {
  2.   console.log("setTimeout");
  3. }, 0);
  4. setImmediate(() => {
  5.   console.log("setImmediate");
  6. });
  7. Promise.resolve().then(() => {
  8.   console.log("Promise then");
  9. });
  10. process.nextTick(() => {
  11.   console.log("process.nextTick");
  12. });
复制代码
输出效果:
  1. process.nextTick
  2. Promise then
  3. setTimeout
  4. 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() in JavaScript
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表