跳到主要内容

JavaScript 计时器

在Web开发中,我们经常需要在特定时间后执行代码,或者定期重复执行某些操作。JavaScript提供了两个核心计时器函数来满足这些需求:setTimeout()setInterval()。理解和掌握这些计时器函数对于创建动画、定时更新内容、延迟执行以及实现各种交互体验至关重要。

计时器的基础概念

JavaScript计时器允许我们控制代码的执行时间。虽然它们不属于JavaScript核心语言,而是由浏览器提供的Web API,但这些计时器函数在几乎所有现代浏览器和Node.js环境中都可用。

主要的计时器函数有两种:

  • setTimeout(): 延迟一段时间后执行一次代码
  • setInterval(): 每隔一段时间重复执行代码

setTimeout() - 延迟执行

setTimeout()函数用于在指定的毫秒数后执行一次代码。

基本语法

javascript
let timerId = setTimeout(function, delay, param1, param2, ...);

参数说明:

  • function: 要执行的函数或代码字符串(推荐使用函数)
  • delay: 延迟的毫秒数(1000毫秒 = 1秒)
  • param1, param2, ...: (可选)传递给函数的参数
  • 返回值: 一个数值ID,可用于通过clearTimeout()取消该计时器

基本示例

javascript
// 2秒后显示一条消息
setTimeout(function() {
console.log("你好,这条消息延迟了2秒显示");
}, 2000);

// 使用箭头函数简化写法
setTimeout(() => {
console.log("使用箭头函数的延迟消息");
}, 2000);

传递参数

javascript
function greet(name, message) {
console.log(`${name}${message}`);
}

// 向延时函数传递参数
setTimeout(greet, 2000, "小明", "欢迎学习JavaScript");
// 2秒后输出: "小明,欢迎学习JavaScript!"

取消计时器

我们可以在计时器触发之前取消它:

javascript
// 存储计时器ID
const timerId = setTimeout(() => {
console.log("这条消息永远不会显示");
}, 5000);

// 在3秒后取消计时器
setTimeout(() => {
clearTimeout(timerId);
console.log("已取消之前的定时器");
}, 3000);

setInterval() - 周期性执行

setInterval()函数用于按照指定的时间间隔重复执行代码。

基本语法

javascript
let intervalId = setInterval(function, delay, param1, param2, ...);

参数与setTimeout()相同,但函数会重复执行,直到被取消。

基本示例

javascript
// 每2秒执行一次
let counter = 0;
const intervalId = setInterval(() => {
counter++;
console.log(`已执行${counter}`);

// 执行5次后停止
if (counter >= 5) {
clearInterval(intervalId);
console.log("计时器已停止");
}
}, 2000);

输出将会是:

已执行1次 (2秒后)
已执行2次 (4秒后)
已执行3次 (6秒后)
已执行4次 (8秒后)
已执行5次 (10秒后)
计时器已停止

停止周期性计时器

javascript
const intervalId = setInterval(() => {
console.log("每秒执行一次");
}, 1000);

// 5秒后停止计时器
setTimeout(() => {
clearInterval(intervalId);
console.log("周期性计时器已停止");
}, 5000);

计时器的注意事项

1. 计时器不保证精确时间

由于JavaScript是单线程的,如果主线程繁忙(例如执行耗时操作),计时器可能无法在精确的时间执行。

javascript
// 演示计时器不精确性
console.log("开始");
setTimeout(() => {
console.log("理论上应该在1秒后执行");
}, 1000);

// 模拟耗时操作
const startTime = Date.now();
while (Date.now() - startTime < 2000) {
// 阻塞2秒
}
console.log("耗时操作结束");

上面的代码中,理论上1秒后应该执行setTimeout的回调,但由于主线程被阻塞了2秒,所以setTimeout回调会在耗时操作结束后立即执行。

2. 最小延迟时间

即使设置延迟为0毫秒,setTimeout(fn, 0)也不会立即执行,而是在当前执行栈清空后才会执行,这通常用于将任务推迟到下一个事件循环。

javascript
console.log("先执行");
setTimeout(() => {
console.log("第三个执行");
}, 0);
console.log("第二个执行");

// 输出:
// 先执行
// 第二个执行
// 第三个执行

3. 嵌套的setTimeout与setInterval的区别

在某些情况下,使用嵌套的setTimeoutsetInterval更合适:

javascript
// 使用setInterval
let intervalCount = 0;
const myInterval = setInterval(() => {
intervalCount++;
console.log(`setInterval: ${intervalCount}`);

// 模拟随机执行时间
const delay = Math.floor(Math.random() * 1000);
let startTime = Date.now();
while (Date.now() - startTime < delay) {}

if (intervalCount >= 5) clearInterval(myInterval);
}, 1000);

// 使用嵌套的setTimeout
let timeoutCount = 0;
function delayedFunction() {
timeoutCount++;
console.log(`setTimeout: ${timeoutCount}`);

// 模拟随机执行时间
const delay = Math.floor(Math.random() * 1000);
let startTime = Date.now();
while (Date.now() - startTime < delay) {}

if (timeoutCount < 5) {
setTimeout(delayedFunction, 1000);
}
}
setTimeout(delayedFunction, 1000);
提示

嵌套setTimeout的优点是,它会在前一个回调函数完成后才开始计时下一次执行,而setInterval则不论前一个回调是否完成,都会尝试每隔固定时间执行一次。当回调执行时间不确定时,嵌套setTimeout可能是更好的选择。

实际应用场景

1. 实现倒计时

javascript
function countdown(seconds) {
const countdownDisplay = document.getElementById('countdown');

const intervalId = setInterval(() => {
countdownDisplay.textContent = seconds;
seconds--;

if (seconds < 0) {
clearInterval(intervalId);
countdownDisplay.textContent = "时间到!";
}
}, 1000);
}

// 使用方式
// <div id="countdown">10</div>
// countdown(10); // 从10秒开始倒计时

2. 实现打字机效果

javascript
function typewriterEffect(element, text, speed = 50) {
let index = 0;

// 清除元素中原有的内容
element.textContent = '';

function type() {
if (index < text.length) {
element.textContent += text.charAt(index);
index++;
setTimeout(type, speed);
}
}

// 开始打字效果
setTimeout(type, speed);
}

// 使用方式
// <p id="typewriter"></p>
// const element = document.getElementById('typewriter');
// typewriterEffect(element, "欢迎学习JavaScript计时器!这是一个模拟打字机的效果。", 100);

3. 轮播图自动切换

javascript
function createSlideshow() {
const slides = document.querySelectorAll('.slide');
let currentSlide = 0;

function showSlide(index) {
// 隐藏所有幻灯片
slides.forEach(slide => slide.style.display = 'none');
// 显示当前幻灯片
slides[index].style.display = 'block';
}

function nextSlide() {
currentSlide = (currentSlide + 1) % slides.length;
showSlide(currentSlide);
}

// 初始显示第一张幻灯片
showSlide(currentSlide);

// 每3秒切换到下一张
setInterval(nextSlide, 3000);
}

// 页面加载后初始化轮播图
// document.addEventListener('DOMContentLoaded', createSlideshow);

4. 防抖(Debounce)实现

防抖是一种常用的性能优化技术,用于限制函数的执行频率。

javascript
function debounce(func, delay) {
let timeoutId;

return function(...args) {
// 取消之前的计时器
clearTimeout(timeoutId);
// 设置新的计时器
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}

// 使用方式
const handleSearch = debounce(function(event) {
const searchTerm = event.target.value;
console.log(`搜索: ${searchTerm}`);
// 执行实际搜索功能...
}, 500);

// 将事件监听器添加到搜索输入框
// const searchInput = document.getElementById('search');
// searchInput.addEventListener('input', handleSearch);

总结

JavaScript计时器是Web开发中不可或缺的工具,它们使我们能够控制代码的执行时间:

  • setTimeout() 用于延迟执行代码
  • setInterval() 用于周期性执行代码
  • clearTimeout()clearInterval() 用于取消计时器

虽然计时器看似简单,但理解它们的行为和限制对于创建有效的Web应用至关重要。计时器与JavaScript的事件循环密切相关,掌握这些知识将帮助你编写更高效、更流畅的前端应用程序。

练习

  1. 创建一个简单的数字时钟,每秒更新一次时间显示。
  2. 实现一个可以暂停、继续和重置的秒表功能。
  3. 创建一个倒计时器,让用户可以设置倒计时的时间。
  4. 实现一个自动保存功能,当用户停止输入2秒后自动保存表单内容。
  5. 使用计时器创建一个简单的游戏,例如"反应速度测试",测量用户点击出现的目标所需时间。

拓展资源

警告

记住,JavaScript是单线程的,所以计时器并不能保证精确的执行时间,特别是在主线程繁忙的情况下。