JavaScript定时器

目录

JavaScript 定时器详解

一、定时器基础

1. setTimeout:单次延迟执行

2. setInterval:周期性执行

3. 清除定时器

二、定时器底层机制

1. 事件循环(Event Loop)

2. 最小延迟限制

三、定时器使用场景

1. 延迟执行

2. 轮询数据

3. 防抖(Debounce)与节流(Throttle)

4. 动画(优先使用 requestAnimationFrame)

JavaScript setInterval 详解

一、基础语法

1. 语法

2. 示例

二、核心问题与解决方案

1. 时间不精确与任务堆积

2. this 指向问题

3. 内存泄漏

三、性能优化与替代方案

1. 高频任务的优化

2. 异步任务的正确处理

3. Web Workers 处理复杂计算

四、应用场景

1. 倒计时组件

2. 轮询服务器状态

五、总结与最佳实践

示例:用户注册倒计时


JavaScript 定时器详解

定时器是 JavaScript 中实现 延迟执行 或 周期性执行 代码的核心工具,常用于动画、轮询、延迟加载等场景。


一、定时器基础

1. setTimeout:单次延迟执行

  • 语法setTimeout(callback, delay, arg1, arg2...)

  • 作用:在指定延迟(毫秒)后执行一次回调函数。

  • 返回值:定时器 ID(用于清除)。

  • 示例

    setTimeout(() => {
      console.log("2秒后执行");
    }, 2000);

2. setInterval:周期性执行

  • 语法setInterval(callback, interval, arg1, arg2...)

  • 作用:每隔指定时间重复执行回调函数。

  • 返回值:定时器 ID(用于清除)。

  • 示例

    const timerId = setInterval(() => {
      console.log("每隔1秒执行");
    }, 1000);

3. 清除定时器

  • clearTimeout(timeoutId):取消 setTimeout 定时器。

  • clearInterval(intervalId):取消 setInterval 定时器。

  • 示例

    const timeoutId = setTimeout(() => {}, 2000);
    clearTimeout(timeoutId); // 取消未执行的定时器
    
    const intervalId = setInterval(() => {}, 1000);
    clearInterval(intervalId); // 停止周期性执行

二、定时器底层机制

1. 事件循环(Event Loop)

JavaScript 是单线程语言,定时器的回调函数会被放入 任务队列,等待主线程空闲时执行。因此:

  • 定时器的时间不精确:回调的实际执行时间 >= 设定的延迟时间。

  • 长时间任务会阻塞定时器:主线程执行耗时操作时,定时器回调会被延迟。

示例

console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
for (let i = 0; i < 1e9; i++) {} // 模拟耗时操作
console.log("End");
// 输出顺序:Start → End → Timeout

2. 最小延迟限制

  • 浏览器setTimeout 和 setInterval 的最小延迟通常为 4ms(嵌套调用时)。

  • Node.js:无固定限制,但受系统调度影响。


三、定时器使用场景

1. 延迟执行

  • 页面加载后延迟操作

    window.addEventListener("load", () => {
      setTimeout(initApp, 1000); // 延迟1秒初始化
    });

2. 轮询数据

  • 周期性请求服务器(慎用,建议改用 WebSocket 或 Server-Sent Events):

    let pollingTimer;
    function pollData() {
      fetch("/api/data").then(handleData);
      pollingTimer = setTimeout(pollData, 5000); // 递归调用
    }
    pollData();
    // 停止轮询:clearTimeout(pollingTimer);

3. 防抖(Debounce)与节流(Throttle)

  • 防抖:高频事件(如输入)停止后执行一次。

    function debounce(fn, delay) {
      let timer;
      return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
      };
    }
    const debouncedSearch = debounce(searchAPI, 300);
    input.addEventListener("input", debouncedSearch);

4. 动画(优先使用 requestAnimationFrame

  • 简单动画(非高性能场景):

    let start = Date.now();
    function animate() {
      const progress = (Date.now() - start) / 1000;
      if (progress < 2) {
        element.style.left = progress * 100 + "px";
        setTimeout(animate, 16); // 模拟60帧(约16ms)
      }
    }
    animate();

  

JavaScript setInterval 详解


一、基础语法

1. 语法

const intervalId = setInterval(callback, delay[, arg1, arg2, ...]);
  • 参数

    • callback: 周期性执行的函数。

    • delay: 间隔时间(单位:毫秒)。

    • arg1, arg2...: 可选参数,传递给回调函数。

  • 返回值:唯一标识定时器的 intervalId,用于清除定时器。

2. 示例

// 每秒输出当前时间
let count = 0;
const intervalId = setInterval(() => {
  console.log(new Date().toLocaleTimeString());
  count++;
  if (count >= 5) {
    clearInterval(intervalId); // 执行5次后停止
  }
}, 1000);

二、核心问题与解决方案

1. 时间不精确与任务堆积

  • 问题setInterval 严格按照间隔时间调度任务,不等待回调完成。若回调执行时间 > delay,任务会堆积。

    setInterval(() => {
      console.log("开始耗时任务");
      for (let i = 0; i < 3e9; i++) {} // 模拟耗时3秒的任务
    }, 1000); // 实际间隔变为至少3秒
  • 解决方案:改用 递归 setTimeout,确保每次任务完成后再调度下一次。

    function repeat() {
      console.log("执行任务");
      setTimeout(repeat, 1000); // 任务完成后再调度
    }
    repeat();

2. this 指向问题

  • 问题:回调中的 this 默认指向全局对象(非严格模式)。

    const obj = {
      value: 10,
      start() {
        setInterval(function() {
          console.log(this.value); // undefined(严格模式)或 window.value
        }, 1000);
      }
    };
    obj.start();
  • 解决方案

    • 箭头函数(推荐):

      setInterval(() => {
        console.log(this.value); // 正确捕获外层 this
      }, 1000);
    • bind 绑定

      setInterval(function() {
        console.log(this.value);
      }.bind(obj), 1000);

3. 内存泄漏

  • 问题:未清除的定时器会持续运行,即使组件已销毁。

    // React 组件示例(错误写法)
    useEffect(() => {
      setInterval(() => {
        console.log("泄漏的定时器");
      }, 1000);
      // ❌ 缺少清理函数
    }, []);
  • 解决方案:在组件销毁时清除定时器。

    // React 正确写法
    useEffect(() => {
      const intervalId = setInterval(/* ... */);
      return () => clearInterval(intervalId); // 清理函数
    }, []);
    
    // 原生 JavaScript
    const intervalId = setInterval(/* ... */);
    window.addEventListener("beforeunload", () => clearInterval(intervalId));

三、性能优化与替代方案

1. 高频任务的优化

  • 问题:高频调用(如 delay < 100ms)可能导致性能问题。

  • 优化策略

    • 合并操作:批量处理数据或 DOM 更新。

    • requestAnimationFrame:用于动画,与屏幕刷新率同步(约60Hz)。

      function animate() {
        element.style.left = (parseFloat(element.style.left) + 1) + "px";
        requestAnimationFrame(animate);
      }
      animate();

2. 异步任务的正确处理

  • 问题setInterval 无法感知异步任务(如 fetch)何时完成。

  • 解决方案:结合 async/await 和递归 setTimeout

    async function fetchData() {
      const data = await fetch("/api/data").then(res => res.json());
      console.log(data);
      setTimeout(fetchData, 1000); // 数据获取完成后再调度
    }
    fetchData();

3. Web Workers 处理复杂计算

  • 场景:避免主线程阻塞。

    // 主线程
    const worker = new Worker("task.js");
    worker.postMessage("start");
    worker.onmessage = e => console.log(e.data);
    
    // task.js
    self.onmessage = () => {
      while (/* 耗时计算 */) {}
      self.postMessage("任务完成");
    };

四、应用场景

1. 倒计时组件

class Countdown {
  constructor(startFrom, onEnd) {
    this.remaining = startFrom;
    this.onEnd = onEnd;
    this.timerId = null;
  }

  start() {
    this.timerId = setInterval(() => {
      this.remaining--;
      console.log(this.remaining);
      if (this.remaining <= 0) {
        this.stop();
        this.onEnd?.();
      }
    }, 1000);
  }

  stop() {
    clearInterval(this.timerId);
    this.timerId = null;
  }
}

// 使用示例
const countdown = new Countdown(10, () => console.log("时间到!"));
countdown.start();

2. 轮询服务器状态

let pollingTimer;

function pollServerStatus() {
  fetch("/api/status")
    .then(res => res.json())
    .then(data => {
      if (data.status === "ready") {
        clearInterval(pollingTimer);
        console.log("服务已就绪");
      }
    });
}

// 每2秒轮询一次
pollingTimer = setInterval(pollServerStatus, 2000);

五、总结与最佳实践

场景 推荐方案 关键点
简单周期性任务 setInterval 及时清理定时器,避免内存泄漏
异步任务/耗时操作 递归 setTimeout + async/await 确保任务完成后再调度
动画/高频渲染 requestAnimationFrame 匹配屏幕刷新率,减少卡顿
复杂计算 Web Workers 避免阻塞主线程
组件内定时器 生命周期钩子清理(如 React/Vue) 防止组件卸载后定时器残留

最佳实践

  1. 优先使用箭头函数:避免 this 指向问题。

  2. 递归 setTimeout 替代 setInterval:解决任务堆积问题。

  3. 严格管理定时器生命周期:在组件/页面销毁时清理。

  4. 高频任务优化:合并操作或使用 requestAnimationFrame


示例:用户注册倒计时


    
    

JavaScript定时器_第1张图片JavaScript定时器_第2张图片

你可能感兴趣的:(javascript,udp,开发语言)