MST

星途 面试题库

面试题:Node.js 中定时器与事件循环如何协同工作

请详细描述 Node.js 定时器(如 setTimeout、setInterval)在事件循环中的执行机制,包括定时器的任务是如何被插入到事件循环队列中的,以及它们何时会被执行。
49.2万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

1. 定时器任务插入机制

  • setTimeoutsetInterval 都是异步操作。当调用 setTimeout(func, delay)setInterval(func, delay) 时,并不会立即执行 func 函数。
  • 它们会在内部创建一个定时器对象,并将这个定时器任务交给 Node.js 的事件循环的定时器模块处理。这里的 delay 参数指定了定时器任务大致的延迟时间(但不是绝对精确的)。这个定时器任务会被安排在未来某个时间点执行。

2. 事件循环概述

  • Node.js 的事件循环是一个持续运行的循环,它会不断检查事件队列,执行其中的任务。事件循环有多个阶段,不同类型的任务会在不同阶段被处理。
  • 主要阶段包括:
    • timers:这个阶段执行 setTimeoutsetInterval 预定的回调函数。
    • pending callbacks:执行一些系统操作的回调,比如 TCP 错误的回调。
    • idle, prepare:仅供内部使用。
    • poll:这个阶段会执行 I/O 相关的回调,同时也会处理新的定时器任务并执行已经到期的定时器回调。
    • check:执行 setImmediate 预定的回调。
    • close callbacks:执行一些关闭相关的回调,比如 socket.on('close', ...)

3. 定时器执行时机

  • 当事件循环进入 timers 阶段时,它会检查定时器队列中已经到期(延迟时间已过)的定时器任务。一旦发现有到期的任务,就会将这些任务的回调函数压入调用栈中执行。
  • 但需要注意的是,定时器的执行时间并非绝对精确等于设定的 delay 时间。原因如下:
    • 事件循环的其他阶段可能会占用较长时间,导致定时器任务不能在刚好 delay 时间后立即执行。例如,poll 阶段可能正在处理大量 I/O 操作,这时即使定时器到期了,也需要等待 poll 阶段处理完当前的 I/O 任务,才会进入 timers 阶段执行定时器任务。
    • 系统的最小延迟限制。不同系统可能存在最小延迟,即使设置的 delay 非常小,实际延迟也会受到系统限制而无法达到那么小的值。
    • 定时器的精度问题。setTimeoutsetInterval 的精度通常在几毫秒左右,并非完全精确到每一毫秒。

对于 setInterval,每次执行完回调函数后,会重新计算下一次执行的时间点,将任务再次插入到定时器队列中,等待下一次事件循环的 timers 阶段执行。如果 setInterval 的回调函数执行时间过长,可能会导致定时器任务之间的间隔被拉长,因为只有当前回调执行完,才会去安排下一次执行。