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

JavaScript手录16-定时器

画板

在JavaScript中,定时器是用于延迟执行代码周期性执行代码的工具,核心有两个API:setTimeout(延迟执行一次)和setInterval(周期性重复执行),它们依赖JavaScript的单线程事件循环机制工作。

一、定时器的核心API

setTimeout:延迟指定时间后执行一次代码

  • 作用:在指定的延迟时间后,执行一次回调函数(或代码片段)。
  • 语法
const timerId = setTimeout(callback, delay, arg1, arg2, ...);
- `callback`:延迟后要执行的函数(必选)。  
- `delay`:延迟时间(单位:毫秒,ms),默认值为0(可选)。  
- `arg1, arg2...`:传递给`callback`的参数(可选)。  
- 返回值:`timerId`(定时器ID,用于后续清除定时器)。
  • 示例
// 3秒后打印"延迟执行"
const timer = setTimeout((msg) => {console.log(msg); // 输出:"延迟执行"
}, 3000, "延迟执行"); 

setInterval:每隔指定时间重复执行代码

  • 作用:每隔delay毫秒,重复执行一次回调函数,直到被手动清除。
  • 语法
const timerId = setInterval(callback, delay, arg1, arg2, ...);
- 参数含义与`setTimeout`一致。  
- 返回值:`timerId`(用于清除定时器)。
  • 示例
// 每隔1秒打印当前时间
let count = 0;
const timer = setInterval(() => {count++;console.log(`${count}次执行:${new Date().toLocaleTimeString()}`);// 执行3次后停止if (count >= 3) {clearInterval(timer);}
}, 1000);

清除定时器:clearTimeoutclearInterval

定时器创建后会一直存在(setTimeout执行后自动失效,setInterval会持续执行),若需提前终止,需用以下方法:

  • clearTimeout(timerId):清除setTimeout创建的定时器。
  • clearInterval(timerId):清除setInterval创建的定时器。

示例:提前清除setTimeout

const timer = setTimeout(() => {console.log("这段代码不会执行");
}, 2000);// 1秒后清除定时器
setTimeout(() => {clearTimeout(timer);console.log("定时器已被清除");
}, 1000);

二、关键特性与注意事项

延迟时间并非“精确执行时间”

  • delay是“最小延迟时间”,而非“精确执行时间”。因为JavaScript是单线程的,若主线程被其他任务(如同步代码、其他回调)阻塞,定时器回调会被放入任务队列等待,实际执行时间会比delay长。 示例
// 同步代码阻塞主线程5秒
console.log("开始");
const start = Date.now();
while (Date.now() - start < 5000) {} // 阻塞5秒// 虽然设置了1秒延迟,但实际会在5秒后(主线程空闲时)执行
setTimeout(() => {console.log("执行了"); // 5秒后才输出
}, 1000);

定时器回调中的this指向

  • 普通函数作为回调时,this默认指向全局对象(浏览器中为window,严格模式下为undefined)。
  • 箭头函数作为回调时,this继承外层作用域的this(与定义时的上下文一致)。 示例
const obj = {name: "测试",func1: function() {// 普通函数回调:this指向windowsetTimeout(function() {console.log(this.name); // 输出:undefined(window无name属性)}, 100);// 箭头函数回调:this继承自func1的this(即obj)setTimeout(() => {console.log(this.name); // 输出:"测试"}, 100);}
};
obj.func1();

setInterval的“累积执行”风险

setInterval会严格按照delay间隔安排下一次执行,若前一次回调执行时间超过delay(如回调内有耗时操作),会导致多个回调堆积,在主线程空闲时连续执行。

解决办法:用setTimeout嵌套调用替代setInterval,确保前一次执行完成后再安排下一次:

// 嵌套setTimeout:避免累积执行
function repeatTask() {console.log("执行任务");// 前一次执行完后,再延迟1秒安排下一次setTimeout(repeatTask, 1000);
}
repeatTask();

延迟时间为0的特殊作用

setTimeout(callback, 0)不会立即执行,而是将回调放入“宏任务队列”,等待主线程同步代码执行完毕后立即执行。常用于:

  • 将耗时操作推迟到主线程空闲时执行,避免阻塞UI渲染。
  • 调整代码执行顺序(让异步代码在同步代码后执行)。

示例

console.log("同步代码1");
setTimeout(() => {console.log("延迟0ms的代码"); // 最后执行(在同步代码后)
}, 0);
console.log("同步代码2");// 输出顺序:同步代码1 → 同步代码2 → 延迟0ms的代码

三、定时器的应用场景

延迟执行:单次延迟后触发操作(setTimeout 为主)

页面加载后的非关键资源初始化

页面核心内容(如首屏DOM、关键样式)加载完成后,延迟初始化非紧急资源(如广告、统计脚本、非首屏组件),避免阻塞主线程,提升首屏加载速度。 (优化用户体验)

// 页面加载完成后,延迟2秒初始化非关键广告组件
window.addEventListener('load', () => {setTimeout(() => {initAdComponent(); // 初始化广告initAnalytics(); // 初始化统计}, 2000);
});
用户操作后的延迟反馈

用户触发操作(如点击、输入)后,延迟执行提示或后续操作,提升交互体验。例如:

  • 按钮点击后延迟禁用(避免快速重复点击);
  • 输入框内容变化后,延迟验证(减少频繁校验的性能消耗)。
// 按钮点击后延迟1秒恢复可点击状态(防止重复提交)
const submitBtn = document.getElementById('submit');
submitBtn.onclick = function() {this.disabled = true; // 立即禁用setTimeout(() => {this.disabled = false; // 1秒后恢复}, 1000);
};
模拟异步操作(测试/调试)

开发中若需模拟后端API的延迟响应(如接口请求耗时),可用 setTimeout 模拟异步等待,无需依赖真实接口。

// 模拟接口请求(延迟1.5秒返回数据)
function mockApiRequest() {return new Promise((resolve) => {setTimeout(() => {resolve({ code: 200, data: '模拟数据' });}, 1500);});
}// 使用:像调用真实接口一样使用
mockApiRequest().then(res => console.log(res));

周期性操作:重复执行某任务(setInterval 或嵌套 setTimeout

实时数据刷新

需定期从后端拉取最新数据的场景(如实时监控面板、聊天消息列表、股票行情),通过定时器周期性请求接口。(轮询)

// 每隔30秒刷新一次用户在线状态
let statusTimer;
function startRefreshStatus() {statusTimer = setInterval(async () => {const res = await fetch('/api/user/status');const status = await res.json();updateStatusUI(status); // 更新UI显示}, 30000); // 30秒一次
}// 页面离开时停止刷新(避免无效请求)
window.addEventListener('beforeunload', () => {clearInterval(statusTimer);
});
倒计时/计时器

秒杀活动、考试时间、订单超时等场景,需实时显示剩余时间,通过定时器每秒更新一次时间戳。

// 倒计时:从10秒开始递减
let countdown = 10;
const countdownEl = document.getElementById('countdown');
const timer = setInterval(() => {countdown--;countdownEl.innerText = `剩余${countdown}`;if (countdown <= 0) {clearInterval(timer);countdownEl.innerText = '时间到!';}
}, 1000);
轮播图/幻灯片自动切换

轮播组件需要每隔固定时间自动切换图片,用定时器控制切换逻辑,同时支持用户交互(如点击、hover)时暂停/恢复。

const carousel = {currentIndex: 0,timer: null,// 启动轮播(每隔5秒切换一张)start() {this.timer = setInterval(() => {this.currentIndex = (this.currentIndex + 1) % 5; // 假设5张图this.switchTo(this.currentIndex); // 切换到当前索引图片}, 5000);},// 暂停轮播(用户hover时)pause() {clearInterval(this.timer);}
};// 初始化轮播
carousel.start();
// 鼠标悬停时暂停,离开时恢复
document.querySelector('.carousel').addEventListener('mouseenter', () => {carousel.pause();
});
document.querySelector('.carousel').addEventListener('mouseleave', () => {carousel.start();
});

优化交互体验:控制操作频率

防抖(Debounce):延迟执行高频操作

对于高频触发的事件(如输入框输入、窗口resize、滚动),通过 setTimeout 延迟执行,避免频繁触发导致的性能问题。(性能优化)例如:

  • 搜索输入框:用户停止输入后才触发搜索请求;
  • 窗口调整:用户停止拖拽窗口后才重新计算布局。
// 搜索输入框防抖:停止输入1秒后才请求接口
let searchTimer;
const searchInput = document.getElementById('search');
searchInput.oninput = function(e) {clearTimeout(searchTimer); // 每次输入都清除上一次的定时器searchTimer = setTimeout(() => {fetch(`/api/search?keyword=${e.target.value}`); // 执行搜索}, 1000); // 1秒内无输入则触发
};
自动保存(如表单、编辑器)

在线文档、表单编辑时,每隔一段时间自动保存内容,避免用户因意外(如刷新、关闭)丢失数据。(数据安全)

// 编辑器每30秒自动保存
let saveTimer;
function startAutoSave() {saveTimer = setInterval(() => {const content = editor.getValue(); // 获取编辑器内容localStorage.setItem('draft', content); // 保存到本地存储showSaveTip(); // 显示“已自动保存”提示}, 30000);
}// 页面关闭前清除定时器
window.addEventListener('beforeunload', () => {clearInterval(saveTimer);
});

动画与过渡效果

简单的数值变化动画(如数字滚动、进度条加载)可通过定时器实现(复杂动画更推荐 requestAnimationFrame,但定时器适合轻量化场景)。 (优化交互体验)

// 进度条从0%动画到100%(1秒完成)
const progressBar = document.getElementById('progress');
let progress = 0;
const timer = setInterval(() => {progress += 1;progressBar.style.width = `${progress}%`;if (progress >= 100) {clearInterval(timer);}
}, 10); // 每10ms更新一次,1秒内完成100步

注意事项(避坑指南)

  1. 及时清除定时器:不再需要的定时器必须用 clearTimeout/clearInterval 清除,否则会导致内存泄漏(尤其是单页应用中组件销毁时)。
  2. 避免 setInterval 累积执行:若回调函数执行时间超过 delay(如耗时操作),setInterval 会导致多个回调堆积。此时推荐用 **嵌套 **setTimeout 替代,确保前一次执行完成后再安排下一次:
// 安全的周期性执行(前一次完成后再延迟)
function safeRepeat() {doHeavyTask(); // 可能耗时的任务setTimeout(safeRepeat, 1000); // 任务完成后再延迟1秒
}
safeRepeat();
  1. 处理 this 指向:定时器回调中的普通函数 this 指向全局对象(window),需用箭头函数或 bind 绑定正确的上下文:
const obj = {name: '测试',start() {// 箭头函数继承obj的thissetTimeout(() => {console.log(this.name); // 正确输出“测试”}, 1000);}
};

四、防抖与节流

节流(Throttle)和防抖(Debounce)是前端开发中用于控制高频事件触发频率的两种优化技术,核心区别在于执行时机和触发规则

  • 防抖(Debounce):事件触发后,等待一段时间再执行函数;如果在这段时间内事件再次触发,则重新计时,直到最后一次触发后等待时间结束才执行。

画板

  • 节流(Throttle):事件触发后,立即执行函数,然后在接下来的一段时间内(冷却期),无论事件触发多少次,都不再执行,直到冷却期结束后才能再次执行。

画板

防抖(Debounce)示例:搜索输入框

场景:用户在搜索框输入时,不需要每次输入都立即请求接口(如输入“苹果”,可能触发“苹”“苹果”多次请求),而是等用户停止输入1秒后再请求,减少无效请求。

代码实现

function debounce(fn, delay) {let timer = null; // 保存定时器IDreturn function(...args) {// 每次触发时,清除上一次的定时器(重新计时)clearTimeout(timer);// 重新设置定时器,延迟delay后执行函数timer = setTimeout(() => {fn.apply(this, args); // 执行目标函数}, delay);};
}// 模拟搜索请求函数
function search(keyword) {console.log(`搜索:${keyword}`);
}// 为输入框绑定防抖处理后的搜索函数(延迟1000ms)
const input = document.getElementById('search-input');
input.oninput = debounce((e) => {search(e.target.value);
}, 1000);

效果

  • 用户快速输入“苹→苹果”,整个过程中输入间隔小于1秒,只会在最后一次输入(“苹果”)后等待1秒,执行一次search("苹果")
  • 如果用户输入“苹”后停顿2秒,会执行search("苹")

节流(Throttle)示例:滚动加载数据

场景:用户滚动页面时,需要监听滚动位置(如判断是否到达底部加载更多),但不需要每次滚动都触发(可能1秒内触发几十次),而是每隔500ms触发一次,避免频繁计算。

代码实现

function throttle(fn, interval) {let lastTime = 0; // 记录上一次执行的时间return function(...args) {const now = Date.now(); // 当前时间戳// 如果当前时间 - 上一次执行时间 >= 间隔时间,则执行if (now - lastTime >= interval) {fn.apply(this, args); // 执行目标函数lastTime = now; // 更新上一次执行时间}};
}// 模拟滚动位置检查函数
function checkScroll() {const scrollTop = document.documentElement.scrollTop;console.log(`滚动位置:${scrollTop}px`);
}// 为窗口绑定节流处理后的滚动函数(间隔500ms)
window.onscroll = throttle(checkScroll, 500);

效果

  • 用户快速滚动页面,1秒内可能触发10次滚动事件,但checkScroll只会在第0ms、500ms、1000ms时执行,即每500ms执行一次

核心区别对比

特性防抖(Debounce)节流(Throttle)
执行时机事件触发后等待一段时间,最后一次触发后执行事件触发后立即执行,之后冷却期内不执行
触发规则多次触发会重置计时,延迟执行多次触发不会重置计时,固定间隔执行
核心目的等待“操作结束”后执行一次控制“持续操作”的执行频率
典型场景搜索输入、窗口resize、按钮防重复点击滚动加载、拖拽尺寸计算、高频点击限制

总结

  • 防抖像“电梯”:按下按钮后,电梯会等10秒关门;如果有人再按,重新等10秒,直到没人按才关门。
  • 节流像“红绿灯”:红灯亮起后,无论多少车来,都要等30秒绿灯才放行,固定间隔执行。

根据场景选择:需要“等待操作结束”用防抖,需要“控制执行频率”用节流。

http://www.dtcms.com/a/329238.html

相关文章:

  • 基于51单片机的手机蓝牙控制8位LED灯亮灭设计
  • 传统Python开发工程师转型大模型智能体开发工程师路径
  • jq实现页面区域内拖动功能
  • InfluxDB 在工业控制系统中的数据监控案例(一)
  • 自然语言处理的实际应用
  • 晓知识: 微服务CAP定理
  • 5. synchronized 关键字 - 监视器锁 monitor lock
  • 基于 MybatisPlus 将百度天气数据存储至 PostgreSQL 数据库的实践
  • 飞算JavaAI云原生实践:基于Docker与K8s的自动化部署架构解析
  • 深入理解 C++ 中的虚函数:原理、特点与使用场景
  • Nginx学习笔记(七)——Nginx负载均衡
  • Orange的运维学习日记--43.Ansible进阶之变量与加密
  • SQL详细语法教程(二)--DML(数据操作语言)和DQL(数据查询语言)
  • 健永科技工业自动化RFID解决方案
  • Linux:线程
  • LeetCode215~ 234题解
  • 《算法导论》第 23 章 - 最小生成树
  • 中高级餐饮服务食品安全员考试核心知识点汇总
  • 亚马逊精准词失灵:广告效能瓶颈的系统破解与矩阵重构
  • RK3588——DMABUF+CMA的完美组合
  • YOLO-v2-tiny 20种物体检测模型
  • 基于C语言基础对C++的进一步学习_C和C++编程范式、C与C++对比的一些补充知识、C++中的命名空间、文件分层
  • Java Redis基础入门:快速上手指南
  • 广东省省考备考(第七十五天8.13)——判断推理(图形推理题型总结)
  • flex布局之设置主轴上的子元素排列方式一
  • 机器学习之词向量转换
  • 【H5】禁止IOS、安卓端长按的一些默认操作
  • ios添加ic卡如何操作?
  • 8.12 数据分析(1)
  • mac 安卓模拟器 blueStacks