您现在的位置是:网站首页 > 定时器与延时器文章详情

定时器与延时器

在JavaScript中,定时器和延时器是控制代码执行时间的重要工具。它们允许开发者在特定时间间隔或延迟后执行函数,从而实现动画、轮询、异步操作等场景。

setTimeout:延时执行

setTimeout用于在指定的毫秒数后执行一次函数。它的基本语法如下:

setTimeout(function, delay, [arg1], [arg2], ...);
  • function:要执行的函数
  • delay:延迟的毫秒数
  • arg1, arg2,...:可选参数,会传递给回调函数

示例1:简单延时

setTimeout(() => {
  console.log('这条消息将在3秒后显示');
}, 3000);

示例2:带参数的延时

function greet(name) {
  console.log(`Hello, ${name}!`);
}

setTimeout(greet, 2000, 'Alice');

setTimeout返回一个定时器ID,可以用于取消这个延时:

const timerId = setTimeout(() => console.log('不会执行'), 1000);
clearTimeout(timerId);

setInterval:定时重复执行

setInterval会按照固定的时间间隔重复执行函数:

setInterval(function, interval, [arg1], [arg2], ...);

示例:每秒更新计数器

let count = 0;
const intervalId = setInterval(() => {
  count++;
  console.log(`计数: ${count}`);
  if (count >= 5) {
    clearInterval(intervalId);
  }
}, 1000);

定时器的执行机制

JavaScript是单线程的,定时器的回调函数会被放入事件队列,只有当主线程空闲时才会执行。这意味着:

  1. 定时器的延迟时间是至少等待时间,不一定是精确时间
  2. 长时间运行的代码会延迟定时器的执行

示例:

console.log('开始');
setTimeout(() => console.log('定时器'), 0);
console.log('结束');

// 输出顺序:
// 开始
// 结束
// 定时器

递归setTimeout vs setInterval

对于需要固定间隔执行的任务,有两种实现方式:

方法1:使用setInterval

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

方法2:递归使用setTimeout

function repeat() {
  console.log('每1秒执行一次');
  setTimeout(repeat, 1000);
}
setTimeout(repeat, 1000);

递归setTimeout的优势:

  • 可以确保每次执行之间的间隔
  • 更容易根据条件停止
  • 可以动态调整下一次执行的时间

定时器的实际应用

实现倒计时功能

function countdown(seconds) {
  let remaining = seconds;
  const timer = setInterval(() => {
    console.log(`${remaining}秒...`);
    remaining--;
    if (remaining < 0) {
      clearInterval(timer);
      console.log('时间到!');
    }
  }, 1000);
}
countdown(5);

实现简单的动画效果

const box = document.createElement('div');
box.style.width = '100px';
box.style.height = '100px';
box.style.backgroundColor = 'red';
document.body.appendChild(box);

let position = 0;
const animate = setInterval(() => {
  position += 5;
  box.style.transform = `translateX(${position}px)`;
  if (position > 300) {
    clearInterval(animate);
  }
}, 50);

实现轮询检查

function checkStatus() {
  fetch('/api/status')
    .then(response => response.json())
    .then(data => {
      if (data.ready) {
        console.log('任务完成');
      } else {
        setTimeout(checkStatus, 1000); // 1秒后再次检查
      }
    });
}
checkStatus();

定时器的高级用法

使用requestAnimationFrame实现动画

对于需要高性能动画的场景,requestAnimationFramesetInterval更合适:

function animate() {
  // 动画逻辑
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

使用Promise封装定时器

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function demo() {
  console.log('开始等待');
  await delay(2000);
  console.log('2秒后');
}
demo();

防抖(debounce)实现

function debounce(func, wait) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, arguments);
    }, wait);
  };
}

// 使用示例
window.addEventListener('resize', debounce(() => {
  console.log('窗口大小改变');
}, 300));

节流(throttle)实现

function throttle(func, limit) {
  let inThrottle;
  return function() {
    if (!inThrottle) {
      func.apply(this, arguments);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// 使用示例
window.addEventListener('scroll', throttle(() => {
  console.log('滚动事件');
}, 1000));

定时器的注意事项

  1. 内存泄漏:忘记清除定时器可能导致内存泄漏

    // 错误示例
    function startTimer() {
      setInterval(() => {
        console.log('内存泄漏风险');
      }, 1000);
    }
    
  2. this绑定:定时器回调中的this可能不是预期的对象

    const obj = {
      value: 42,
      showValue: function() {
        setTimeout(function() {
          console.log(this.value); // undefined
        }, 100);
      }
    };
    

    解决方法:

    // 使用箭头函数
    const obj = {
      value: 42,
      showValue: function() {
        setTimeout(() => {
          console.log(this.value); // 42
        }, 100);
      }
    };
    
  3. 最小延迟限制:浏览器对嵌套定时器有最小延迟限制(通常4ms)

  4. 后台标签页限制:浏览器可能会降低后台标签页中定时器的执行频率

定时器的替代方案

对于复杂的时间控制需求,可以考虑:

  1. Web Workers:在后台线程执行定时任务
  2. requestAnimationFrame:更适合动画场景
  3. RxJS等响应式编程库提供更强大的时间控制能力

性能优化建议

  1. 避免在热代码路径中频繁创建和销毁定时器
  2. 多个定时器可以合并为一个
  3. 使用performance.now()获取高精度时间戳
  4. 对于不需要精确计时的场景,可以适当增加间隔时间
// 性能优化示例
const startTime = performance.now();
let lastUpdate = startTime;

function update(currentTime) {
  const delta = currentTime - lastUpdate;
  if (delta > 1000/60) { // 约60FPS
    // 执行更新逻辑
    lastUpdate = currentTime;
  }
  requestAnimationFrame(update);
}
requestAnimationFrame(update);

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步