Skip to content

06setTimeout 与 setInterval 区别

上次更新 2025年11月29日星期六 5:36:17 字数 0 字 时长 0 分钟

基本概念

setTimeout

  • 定义: 在指定的延迟时间后执行一次函数
  • 语法: setTimeout(callback, delay, ...args)
  • 返回值: 返回一个定时器 ID,可用于取消定时器
javascript
const timerId = setTimeout(() => {
  console.log('执行一次');
}, 1000);

// 取消定时器
clearTimeout(timerId);

setInterval

  • 定义: 按照指定的周期(以毫秒计)重复调用函数
  • 语法: setInterval(callback, delay, ...args)
  • 返回值: 返回一个定时器 ID,可用于取消定时器
javascript
const intervalId = setInterval(() => {
  console.log('重复执行');
}, 1000);

// 取消定时器
clearInterval(intervalId);

核心区别

1. 执行次数

  • setTimeout: 只执行一次
  • setInterval: 重复执行,直到被清除

2. 执行时机

  • setTimeout: 延迟指定时间后执行一次
  • setInterval: 每隔指定时间重复执行
javascript
// setTimeout - 执行一次
setTimeout(() => {
  console.log('1秒后执行一次');
}, 1000);

// setInterval - 每隔1秒执行
setInterval(() => {
  console.log('每隔1秒执行一次');
}, 1000);

3. 时间间隔的计算方式

setTimeout:

  • 从上一次执行完成后开始计时
  • 时间间隔 = 延迟时间 + 执行时间

setInterval:

  • 从上一次执行开始时计时
  • 时间间隔 = 延迟时间(不考虑执行时间)
javascript
// setTimeout 递归实现定时
function mySetTimeout() {
  console.log('执行任务');
  // 任务执行完后,再设置下一次定时
  setTimeout(mySetTimeout, 1000);
}
setTimeout(mySetTimeout, 1000);

// setInterval 直接定时
setInterval(() => {
  console.log('执行任务');
  // 不管任务是否执行完,都会按固定间隔触发
}, 1000);

重要问题

setInterval 的问题

1. 任务堆积问题

如果某次任务执行时间过长,可能导致多个任务堆积:

javascript
// 错误示例:任务执行时间超过间隔时间
setInterval(() => {
  // 假设这个任务需要 2 秒
  heavyTask(); // 耗时 2000ms
}, 1000); // 但间隔只有 1 秒

// 问题:任务还没执行完,下一个任务就被加入队列

2. 无法保证执行间隔

setInterval 不会等待上一次任务完成,可能导致实际间隔不准确。

javascript
let count = 0;
setInterval(() => {
  console.log('开始:', new Date().getTime());
  // 模拟耗时操作
  for(let i = 0; i < 1000000000; i++) {}
  console.log('结束:', new Date().getTime());
  count++;
  if(count > 3) clearInterval(this);
}, 1000);

// 输出会发现实际间隔远大于 1000ms

setTimeout 递归实现定时的优势

javascript
function myInterval() {
  console.log('执行任务');
  
  // 任务执行完成后,再设置下一次定时
  setTimeout(() => {
    myInterval(); // 递归调用
  }, 1000);
}

// 启动
setTimeout(myInterval, 1000);

优势:

  1. 确保每次任务执行完后,才开始计时
  2. 避免任务堆积
  3. 可以根据上一次执行结果动态调整间隔
javascript
// 动态调整间隔时间
function dynamicInterval(delay) {
  console.log('执行任务');
  
  setTimeout(() => {
    // 根据条件调整下次执行的延迟时间
    const nextDelay = someCondition ? 1000 : 2000;
    dynamicInterval(nextDelay);
  }, delay);
}

dynamicInterval(1000);

实际应用场景

适合使用 setTimeout 的场景

  1. 延迟执行: 延迟显示提示信息
javascript
setTimeout(() => {
  showNotification('操作成功');
}, 2000);
  1. 防抖 (Debounce): 用户输入完成后再执行
javascript
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}
  1. 递归定时任务: 确保任务按序执行
javascript
function pollData() {
  fetchData().then(data => {
    processData(data);
    // 处理完成后再设置下一次
    setTimeout(pollData, 5000);
  });
}

适合使用 setInterval 的场景

  1. 固定频率更新: 时钟显示
javascript
setInterval(() => {
  updateClock();
}, 1000);
  1. 动画效果: 简单的帧动画(但建议用 requestAnimationFrame)
javascript
setInterval(() => {
  moveElement();
}, 16); // 约 60fps
  1. 轮询: 定时检查状态(简单场景)
javascript
const checkStatus = setInterval(() => {
  if (isComplete()) {
    clearInterval(checkStatus);
  }
}, 1000);

性能对比

javascript
// 测试 setInterval
console.time('setInterval');
let count1 = 0;
const interval = setInterval(() => {
  count1++;
  if (count1 === 10) {
    clearInterval(interval);
    console.timeEnd('setInterval');
  }
}, 100);

// 测试 setTimeout 递归
console.time('setTimeout');
let count2 = 0;
function recursiveTimeout() {
  count2++;
  if (count2 === 10) {
    console.timeEnd('setTimeout');
    return;
  }
  setTimeout(recursiveTimeout, 100);
}
setTimeout(recursiveTimeout, 100);

注意事项

1. 最小延迟时间

浏览器对定时器有最小延迟限制:

  • HTML5 规范要求最小 4ms
  • 嵌套层级超过 5 层后,最小间隔为 4ms
  • 未激活的标签页可能延长到 1000ms
javascript
// 延迟可能不是真的 0ms
setTimeout(() => {
  console.log('延迟执行');
}, 0); // 实际延迟约 4ms

2. this 指向问题

javascript
const obj = {
  name: '对象',
  method() {
    // 错误:this 指向 window
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 1000);
    
    // 正确:使用箭头函数
    setTimeout(() => {
      console.log(this.name); // '对象'
    }, 1000);
  }
};

3. 内存泄漏风险

javascript
// 错误:忘记清除定时器
const interval = setInterval(() => {
  // 一些操作
}, 1000);

// 正确:在组件卸载时清除
// Vue 示例
export default {
  data() {
    return {
      timer: null
    }
  },
  mounted() {
    this.timer = setInterval(() => {
      // 操作
    }, 1000);
  },
  beforeUnmount() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }
}

总结

特性setTimeoutsetInterval
执行次数一次重复执行
时间计算从执行完成后开始计时从执行开始时计时
堆积问题无(递归使用时)可能产生
灵活性高(可动态调整)低(固定间隔)
使用场景延迟执行、防抖节流固定频率更新、轮询
推荐程度⭐⭐⭐⭐⭐⭐⭐⭐

最佳实践:

  • 对于需要重复执行的任务,优先考虑使用 setTimeout 递归实现
  • setInterval 适用于简单的固定频率任务
  • 记得在适当的时候清除定时器,避免内存泄漏
  • 对于动画,优先使用 requestAnimationFrame
关注公众号