20. setTimeout 和 setInterval 的陷阱 副本.md

查看飞书原文 ↗4,769 字符

1. 核心概念 (Core Concept)

setTimeout 和 setInterval 是 JavaScript 中用于处理延迟或重复执行代码的两个核心函数。它们都利用了浏览器的事件循环机制来异步地调度函数的执行。

2. 为什么需要它? (The "Why")

这两个函数是实现异步或定时任务的基础,它们:

3. API 与用法 (API & Usage)

setTimeout(callback, delay, ...args)

function greet(name) {
  console.log('Hello, ' + name);
}

const timerId = setTimeout(greet, 2000, 'World'); // 2秒后执行 greet('World')

// 可以取消这个延时执行
// clearTimeout(timerId);

setInterval(callback, delay, ...args)

let count = 0;
const intervalId = setInterval(() => {
  count++;
  console.log(`Interval fired ${count} times`);
  if (count >= 5) {
    clearInterval(intervalId); // 达到条件时停止重复
  }
}, 1000); // 每秒执行一次

4. 关键注意事项 (Key Considerations)

4.1. 延迟不是精确的 (Delay is Not Guaranteed)

4.2. setInterval 可能导致任务堆积 (Potential Task Queuing/Stack Up with setInterval)

// 避免 setInterval 堆积的递归 setTimeout 方法
function recursiveTimeout() {
  // 执行任务...
  console.log("Executing task...");

  // 模拟一个耗时操作 (> 100ms)
  let start = Date.now();
  while (Date.now() - start < 200);

  // 任务执行完成后再调度下一个
  setTimeout(recursiveTimeout, 100); // 永远保持100ms的间隔 (在任务完成后)
}

// 注释掉,避免实际运行
// recursiveTimeout();

4.3. this 的指向问题 (this Context Issues)

class MyClass {
  constructor() {
    this.value = 'initial';
  }

  // 问题: 传统函数,this 指向 window 或 undefined
  methodWithProblem() {
    setTimeout(function() {
      // console.log(this.value); // Error: this 指向不对
    }, 100);
  }

  // 解决方案 1: 箭头函数 (推荐)
  methodWithArrow() {
    setTimeout(() => {
      console.log(this.value); // logs 'initial'
    }, 100);
  }

  // 解决方案 2: bind
  methodWithBind() {
    setTimeout(function() {
      console.log(this.value);
    }.bind(this), 100); // 绑定 this
  }
}

const instance = new MyClass();
// instance.methodWithProblem(); // 会有问题
instance.methodWithArrow();
instance.methodWithBind();

4.4. 取消定时器 (clearTimeout / clearInterval)

let intervalId = null;

function startFetching() {
  intervalId = setInterval(() => {
    console.log('Fetching data...');
    // ... fetch logic ...
  }, 5000);
}

function stopFetching() {
  if (intervalId !== null) {
    console.log('Stopping fetch interval');
    clearInterval(intervalId);
    intervalId = null; // 清理引用
  }
}

// 开始获取 (例如,在组件挂载时)
// startFetching();

// 停止获取 (例如,在组件卸载时)
// stopFetching();

5. 参考资料 (References)


原文链接与附件索引