定时器(Timer)和延时器
在 JavaScript 和 React 开发中,定时器(Timer)和延时器(Delay,通常指 setTimeout
) 是非常常用的功能,用于在指定的时间后执行某些操作,比如:
- 延迟显示弹窗
- 定时轮询数据
- 轮播图自动切换
- 倒计时
- 组件挂载后延迟聚焦、滚动等
但与此同时,定时器的管理(特别是清理/关闭定时器)也非常重要,否则容易导致:
- 内存泄漏
- 定时器回调函数中访问了已经销毁的组件或变量(报错)
- 逻辑混乱,比如组件卸载后仍然执行了定时任务
✅ 一、JavaScript 中的定时器方法
1. **setTimeout
—— 延时器(延迟执行,只执行一次)**
在指定的延迟时间(毫秒)后,执行一次某个函数。
📌 基本语法:
const timerId = setTimeout(() => {console.log('这条消息将在 3 秒后显示');
}, 3000); // 3000 毫秒 = 3 秒
setTimeout
返回一个 定时器 ID(timerId),可用于后续取消这个定时任务
2. **clearTimeout
—— 取消延时器**
用于取消一个尚未执行的
setTimeout
定时任务。
📌 基本语法:
clearTimeout(timerId);
3. **setInterval
—— 定时器(每隔一段时间执行一次,可多次执行)**
每隔指定的时间(毫秒),重复执行某个函数,直到被取消。
📌 基本语法:
const intervalId = setInterval(() => {console.log('这条消息每隔 1 秒显示一次');
}, 1000); // 每 1000ms(1秒)执行一次
- 同样会返回一个 定时器 ID(intervalId),用于取消定时任务
4. **clearInterval
—— 取消定时器**
用于停止一个通过
setInterval
创建的重复执行的定时任务。
📌 基本语法:
clearInterval(intervalId);
✅ 二、在 React 中使用定时器的最佳实践(重点!)
在 React(尤其是函数组件)中,使用定时器时,你必须手动管理定时器的生命周期,确保在组件销毁时清除定时器,避免内存泄漏和无效操作。
🎯 常见场景:组件挂载后启动定时器,组件卸载前清除定时器
✅ 示例 1:使用 useEffect
启动 setTimeout
,并在组件卸载时清除
import React, { useEffect } from 'react';function DelayedMessage() {useEffect(() => {// 设定一个延时器:3秒后打印消息const timerId = setTimeout(() => {console.log('3 秒后这条消息被打印出来了');}, 3000);// ✅ 清理函数:组件卸载时清除定时器return () => {clearTimeout(timerId);console.log('组件卸载,定时器已清除');};}, []); // 空依赖,只在挂载时运行一次return <div>页面加载后,3秒后控制台会打印一条消息</div>;
}export default DelayedMessage;
✅ 示例 2:使用 setInterval
做倒计时或轮询,记得清除!
import React, { useEffect, useState } from 'react';function Countdown() {const [count, setCount] = useState(5);useEffect(() => {// 每秒减 1,直到 count <= 0const intervalId = setInterval(() => {setCount((prev) => {if (prev <= 1) {clearInterval(intervalId); // 可以在这里清除,但最好还是在 useEffect 的清理函数中统一清除return 0;}return prev - 1;});}, 1000);// ✅ 清理函数:组件卸载时清除定时器return () => {clearInterval(intervalId);console.log('组件卸载,定时器已清除');};}, []); // 空依赖,只在挂载时启动一次return <div>倒计时:{count} 秒</div>;
}export default Countdown;
✅ 三、关键要点总结
操作 | 方法 | 说明 |
---|---|---|
延时执行(一次) | setTimeout(callback, delay) | 延迟 delay 毫秒后执行 callback |
取消延时器 | clearTimeout(timerId) | 取消尚未执行的定时任务 |
定时执行(重复) | setInterval(callback, interval) | 每隔 interval 毫秒重复执行 callback |
取消定时器 | clearInterval(intervalId) | 停止重复执行的定时任务 |
React 中使用 | 在 useEffect 中启动,在 清理函数(return 的函数) 中清除 | 避免内存泄漏,非常重要! |
✅ 四、React + 定时器 使用 checklist(最佳实践清单)
步骤 | 是否做到 | 说明 |
---|---|---|
✅ 1 | 在 useEffect 中启动定时器 | 保证定时器在组件挂载后执行 |
✅ 2 | 保存定时器 ID(比如 const id = setTimeout(...) ) | 以便后续清除 |
✅ 3 | 在 useEffect 的 返回函数(清理函数)中清除定时器 | 使用 clearTimeout(id) 或 clearInterval(id) |
✅ 4 | 确保依赖项正确(比如 [] 只在挂载时运行) | 避免定时器重复创建 |
✅ 5 | 不要在定时器回调中直接使用可能已销毁的 state / props | 如果要用,确保逻辑安全 |
❌ 常见错误示例(不要这样做!)
❌ 错误 1:没有清除定时器
useEffect(() => {setTimeout(() => {console.log('这条消息可能会在组件卸载后执行!');}, 3000);
}, []);
⚠️ 问题: 如果组件在 3 秒内卸载了,这个定时器的回调仍然会试图执行,可能会导致访问已销毁的组件状态,甚至报错。
❌ 错误 2:没有保存定时器 ID,所以无法清除
useEffect(() => {setTimeout(() => {}, 1000);// 没有保存返回值,无法清除return () => {// 没有 timerId,无法调用 clearTimeout};
}, []);
✅ 五、完整示例:延时显示 / 隐藏某个组件
import React, { useState, useEffect } from 'react';function Toast({ message }) {const [show, setShow] = useState(false);useEffect(() => {if (message) {setShow(true);const timer = setTimeout(() => {setShow(false);}, 3000); // 3秒后隐藏return () => {clearTimeout(timer);};}}, [message]);if (!show) return null;return <div style={{ background: 'lightblue', padding: 10 }}>{message}</div>;
}export default Toast;
使用方式:
<Toast message="这是一条提示消息,3秒后消失" />
✅ 六、总结一句话:
在 JavaScript 中,
setTimeout
用于延时执行,setInterval
用于重复定时执行,分别用clearTimeout
和clearInterval
来取消它们。
在 React 函数组件中,必须在useEffect
中启动定时器,并在清理函数(return 的函数)中清除定时器,以避免内存泄漏和无效操作,这是定时器使用的核心最佳实践。