面试题答案
一键面试1. 定时器任务插入机制
setTimeout
和setInterval
都是异步操作。当调用setTimeout(func, delay)
或setInterval(func, delay)
时,并不会立即执行func
函数。- 它们会在内部创建一个定时器对象,并将这个定时器任务交给 Node.js 的事件循环的定时器模块处理。这里的
delay
参数指定了定时器任务大致的延迟时间(但不是绝对精确的)。这个定时器任务会被安排在未来某个时间点执行。
2. 事件循环概述
- Node.js 的事件循环是一个持续运行的循环,它会不断检查事件队列,执行其中的任务。事件循环有多个阶段,不同类型的任务会在不同阶段被处理。
- 主要阶段包括:
- timers:这个阶段执行
setTimeout
和setInterval
预定的回调函数。 - pending callbacks:执行一些系统操作的回调,比如 TCP 错误的回调。
- idle, prepare:仅供内部使用。
- poll:这个阶段会执行 I/O 相关的回调,同时也会处理新的定时器任务并执行已经到期的定时器回调。
- check:执行
setImmediate
预定的回调。 - close callbacks:执行一些关闭相关的回调,比如
socket.on('close', ...)
。
- timers:这个阶段执行
3. 定时器执行时机
- 当事件循环进入
timers
阶段时,它会检查定时器队列中已经到期(延迟时间已过)的定时器任务。一旦发现有到期的任务,就会将这些任务的回调函数压入调用栈中执行。 - 但需要注意的是,定时器的执行时间并非绝对精确等于设定的
delay
时间。原因如下:- 事件循环的其他阶段可能会占用较长时间,导致定时器任务不能在刚好
delay
时间后立即执行。例如,poll
阶段可能正在处理大量 I/O 操作,这时即使定时器到期了,也需要等待poll
阶段处理完当前的 I/O 任务,才会进入timers
阶段执行定时器任务。 - 系统的最小延迟限制。不同系统可能存在最小延迟,即使设置的
delay
非常小,实际延迟也会受到系统限制而无法达到那么小的值。 - 定时器的精度问题。
setTimeout
和setInterval
的精度通常在几毫秒左右,并非完全精确到每一毫秒。
- 事件循环的其他阶段可能会占用较长时间,导致定时器任务不能在刚好
对于 setInterval
,每次执行完回调函数后,会重新计算下一次执行的时间点,将任务再次插入到定时器队列中,等待下一次事件循环的 timers
阶段执行。如果 setInterval
的回调函数执行时间过长,可能会导致定时器任务之间的间隔被拉长,因为只有当前回调执行完,才会去安排下一次执行。