全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-15 8 分钟 ✍️ juanwangdev

Node.js 定时器与延迟执行

定时器是 Node.js 异步调度的基础,理解其内部机制对性能优化至关重要。

setTimeout 深度解析

JavaScript
// setTimeout 返回 Timeout 对象
const timer = setTimeout(() => {
  console.log('执行');
}, 1000);

// Timeout 对象属性
console.log(timer._idleTimeout);    // 延迟时间
console.log(timer._onTimeout);      // 回调函数

延迟精度问题

JavaScript
// 实际延迟可能超过指定时间
setTimeout(() => {
  console.log('实际延迟:', Date.now() - start);
}, 100);

// 原因:
// 1. 事件循环阻塞
// 2. timers 阶段执行时机
// 3. 系统调度精度

延迟范围限制

JavaScript
// 延迟时间范围:1ms - 2147483647ms(约 24.8 天)
setTimeout(() => {}, 0);         // 实际最小 1ms
setTimeout(() => {}, 2147483647); // 最大值
setTimeout(() => {}, 2147483648); // 被修正为 1ms

refresh 重置定时器

JavaScript
const timer = setTimeout(() => {
  console.log('执行');
}, 1000);

// 重置定时器,重新开始计时
timer.refresh();

// 延迟重置为 1000ms

setInterval 深度解析

JavaScript
// setInterval 执行间隔问题
setInterval(() => {
  // 如果回调耗时超过间隔
  // 下次执行会延迟
  heavyOperation(); // 耗时 2 秒
}, 1000); // 实际间隔变成 2 秒

间隔累积问题

JavaScript
// setInterval 不会累积延迟
setInterval(() => {
  console.log('tick');
}, 100);

// 如果某次延迟 500ms
// 只执行一次,不会执行 5 次

动态间隔执行

JavaScript
// 使用 setTimeout 实现动态间隔
function dynamicInterval(fn, delay) {
  setTimeout(() => {
    fn();
    dynamicInterval(fn, delay);
  }, delay);
}

// 或在回调中调整延迟
let currentDelay = 1000;
function adaptiveInterval() {
  setTimeout(() => {
    doWork();
    currentDelay = calculateNewDelay();
    adaptiveInterval();
  }, currentDelay);
}

setImmediate

JavaScript
// setImmediate 在 check 阶段执行
setImmediate(() => {
  console.log('immediate');
});

// 与 setTimeout 0 的区别
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
// 在 I/O 回调中: immediate 先于 timeout

setImmediate vs setTimeout

JavaScript
// timers 阶段执行顺序
// setTimeout(fn, 0) → timers 阶段
// setImmediate(fn) → check 阶段

// poll 阶段后的顺序
// poll → check (setImmediate) → timers (setTimeout)
// poll → timers (setTimeout) → check (setImmediate)

// 取决于 poll 阶段计算何时检查 timers

定时器对象操作

unref 自动取消

JavaScript
// unref:允许事件循环退出(无其他活跃句柄时)
const timer = setTimeout(() => {}, 1000);
timer.unref();

// 如果只有这个定时器,事件循环会退出
// 不等待定时器执行

// ref:恢复正常
timer.ref();

hasRef 检查

JavaScript
const timer = setTimeout(() => {}, 1000);
console.log(timer.hasRef()); // true

timer.unref();
console.log(timer.hasRef()); // false

定时器清除

JavaScript
// clearTimeout/clearInterval
const timer1 = setTimeout(() => {}, 1000);
clearTimeout(timer1);

const timer2 = setInterval(() => {}, 1000);
clearInterval(timer2);

// 清除后定时器对象仍存在,但不会执行
console.log(timer1._idleTimeout); // -1(已清除)

定时器队列

JavaScript
// Node.js 使用最小堆管理定时器
// 到期时间最早的定时器最先执行

setTimeout(() => console.log('100ms'), 100);
setTimeout(() => console.log('50ms'), 50);
setTimeout(() => console.log('200ms'), 200);

// 输出顺序: 50ms -> 100ms -> 200ms

定时器精度对比

方法最小延迟精度阶段
setTimeout1ms事件循环影响timers
setInterval1ms回调耗时影响timers
setImmediate-poll 后立即check
process.nextTick-当前操作后-

性能考量

JavaScript
// ❌ 高频定时器
setInterval(() => {
  // 每 1ms 执行,影响性能
}, 1);

// ✅ 合理间隔
setInterval(() => {
  // 最低建议 10ms 以上
}, 100);

// ✅ 按需执行
function scheduleNext() {
  setTimeout(() => {
    if (needWork) {
      doWork();
      scheduleNext();
    }
  }, delay);
}

定时器调试

JavaScript
// 查看活跃定时器
process._getActiveHandles().forEach(handle => {
  if (handle._idleTimeout > 0) {
    console.log('定时器:', handle._idleTimeout);
  }
});

// 使用 debug 工具
const asyncHooks = require('async_hooks');
const hook = asyncHooks.createHook({
  init(asyncId, type) {
    if (type === 'TIMER') {
      console.log('定时器创建:', asyncId);
    }
  }
});
hook.enable();

要点总结

  • setTimeout 延迟范围 1ms-24.8天,精度受事件循环影响
  • setInterval 回调耗时会影响实际间隔
  • setImmediate 在 check 阶段执行,不依赖时间
  • unref 定时器不阻止事件循环退出
  • 使用 setTimeout 递归替代 setInterval 实现动态间隔
  • 高频定时器影响性能,建议最低 10ms 以上

📝 发现内容有误?点击此处直接编辑

← 上一篇 Node.js 事件驱动与EventEmitter
下一篇 → Node.js 并发模型与线程池
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库