当前位置: 首页 > news >正文

【JavaScript】setTimeout和setInterval中的陷阱

✅ 一、核心区别回顾

setTimeout(fn, delay)      // delay 毫秒后执行 fn,一次
setInterval(fn, delay)     // 每隔 delay 毫秒执行 fn,循环执行

⚠️ 二、常见陷阱和注意事项

1. 定时不准(延时不准确)

现象:

你设置了 setTimeout(fn, 1000),但实际执行时间可能远远大于 1000ms。

原因:

JavaScript 是单线程的,setTimeoutsetInterval 都是任务调度器,它们的时间是最短延时,不是准确执行时间。

取决于事件循环,回调需要等待主线程执行完毕。浏览器中常见的最小延迟时间为 4ms ,避免过渡消耗资源。

setTimeout(() => {console.log('Start');
}, 1000);// 如果前面有一个长时间任务
const now = Date.now();
while (Date.now() - now < 3000) {} // 阻塞3秒

➡️ setTimeout 实际会在 3 秒后才执行。


2. setInterval 累积延迟 & 非精确调度

原因:

setInterval 并不等待上一个任务完成,而是按固定时间调度下一次:

setInterval(() => {const start = Date.now();while (Date.now() - start < 1500) {}console.log('执行了一次');
}, 1000);

➡️ 执行任务需要 1.5s,但是设置interval 为 1s ,回调执行时间 .> delay。输出结果不是每隔 1 秒,而是任务积压执行,可能造成线程拥堵,主线程空闲时,任务接连执行。

更推荐使用递归 setTimeout 实现“精准 setInterval”

这样下一次任务的执行总是基于上一次任务完成后的时间来计算。

function repeat() {setTimeout(() => {console.log('更精准的定时任务');repeat(); // 递归调用}, 1000);
}
repeat();

3. this 指向问题

const obj = {name: 'Alice',sayHi() {setTimeout(function () {console.log(this.name); // undefined}, 1000);}
};obj.sayHi();
原因:

普通函数内的 this 指向全局对象(严格模式下是 undefined

✅ 解决方法:

// 方法1:使用箭头函数
setTimeout(() => {console.log(this.name);
}, 1000);// 方法2:bind(this)
setTimeout(function () {console.log(this.name);
}.bind(this), 1000);

4. 内存泄漏风险(setInterval 忘记清除)

setInterval(() => {// 持续引用某个DOM或数据对象
}, 1000);
// 没有 clearInterval => 永远运行,占用内存

✅ 解决方式:

const id = setInterval(fn, 1000);
// 在合适的时候清除
clearInterval(id);

5. setTimeout 最小延时是 4ms(嵌套层次高于5)

// 嵌套层数 >= 5 时,延迟会被强制设置为至少 4ms(浏览器安全策略)

6. setTimeout(fn, 0) ≠ 立即执行

虽然你写了 0 毫秒延时,实际上它会在当前调用栈清空后再执行。

console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// 输出:1 -> 3 -> 2

✅ 三、实战建议

场景建议
需要周期任务,且任务耗时很短可用 setInterval
需要精准间隔、避免堆积使用递归 setTimeout
需要延迟执行一次任务setTimeout(fn, delay)
需要手动停止保留定时器ID,使用 clearTimeout / clearInterval
http://www.lqws.cn/news/568549.html

相关文章:

  • 数据挖掘、机器学习与人工智能:概念辨析与应用边界
  • Linux基本命令篇 —— cal命令
  • 模型预测控制专题:基于增量模型的无差拍预测电流控制
  • Rust 和C++工业机器人实践
  • React与Vue的主要区别
  • 数据分析标普500
  • 打造地基: App拉起基础小程序容器
  • 【AOSP专题】07. FART脱壳-02
  • Python训练营-Day45-tensorboard
  • 设计模式精讲 Day 18:备忘录模式(Memento Pattern)
  • 如何搭建基于RK3588的边缘服务器集群?支持12个RK3588云手机
  • FAST-LIO2源码分析-状态预测
  • 二叉树进阶刷题02(非递归的前中后序遍历)
  • libevent(2)之使用教程(1)介绍
  • 【分析学】 从闭区间套定理出发(二) -- 闭区间连续函数性质
  • 【WRF-Urban数据集】 WRF 模型城市冠层参数 GloUCP 的使用
  • 1 Studying《Computer Vision: Algorithms and Applications 2nd Edition》11-15
  • 【修电脑的小记录】连不上网
  • 强制IDEA始终使用Java 8
  • (24)如何在 Qt 里创建 c++ 类,以前已经学习过如何在 Qt 里引入资源图片文件。以及如何为继承于 Qt已有类的自定义类重新实现虚函数
  • Java面试宝典:基础二
  • 进阶向:Django入门,从零开始构建一个Web应用
  • 面试准备first
  • 【C++11】异常
  • [Linux入门] Linux LVM与磁盘配额入门指南
  • Tomcat 安装使用教程
  • 大数据Hadoop之——安装部署hadoop
  • 深入剖析Nacos服务发现与注册,及如何基于LoadBalancer实现负载均衡
  • 大事件项目记录13-接口开发-补充
  • 【如何实现分布式压测中间件】