前言
最近刚好遇到了计时器相关的需求,那么就浅研究下,不同定时器的原理和异同。众所周知js定时器有三种 setTimeout和setInterval 还有一种不太常用的requeTanimationFrame,
那么接下来我就简单的介绍一下,都是什么定时器,又要如何使用,在什么场景使用,以及三者的区别。
一、setTimeout定时器
1.1 定义
setTimeout是JavaScript中的一个定时器函数,用于在指定的时间后执行一次指定的函数或代码块。
1.2 语法
setTimeout(function, milliseconds, param1, param2, ...)
参数说明:
function:必需,要执行的函数或代码块。
milliseconds:必需,延迟的毫秒数。
param1, param2, ...:可选,传递给函数的参数。
1.3 使用场景
setTimeout适用于需要延迟执行某些操作的场景,比如:
1.延迟执行某个函数或代码块。
2.延迟执行某个动画效果。
3.延迟执行某个异步操作。
1.4 优缺点
优点:
1.简单易用,语法简单。
2.可以设置延迟时间,灵活性高。
缺点:
1.由于JavaScript是单线程执行的,如果setTimeout中的代码执行时间过长,会影响后续代码的执行。
2.如果设置的延迟时间不准确,可能会导致代码执行不稳定。
3.如果需要多次执行某个操作,需要多次调用setTimeout函数。
1.5 示例代码
// 延迟执行某个函数
setTimeout(function(){
console.log('Hello World!');
}, 1000);
// 延迟执行某个动画效果
var box = document.getElementById('box');
setTimeout(function(){
box.style.width = '200px';
box.style.height = '200px';
}, 1000);
// 延迟执行某个异步操作
setTimeout(function(){
$.ajax({
url: 'http://www.example.com',
success: function(data){
console.log(data);
}
});
}, 1000);
二、setInterval定时器
2.1 定义
setInterval是JavaScript中的一个定时器函数,用于每隔一定时间执行一次指定的函数或代码块。
2.2 语法
setInterval(function, milliseconds, param1, param2, ...)
参数说明:
function:必需,要执行的函数或代码块。
milliseconds:必需,每隔多少毫秒执行一次。
param1, param2, ...:可选,传递给函数的参数。
2.3 使用场景
setInterval适用于需要每隔一定时间执行某些操作的场景,比如:
1.定时更新某个数据。
2.定时执行某个动画效果。
3.定时检查某个状态。
2.4 优缺点
优点:
1.可以每隔一定时间执行某个操作,方便实现定时任务。
2.可以设置间隔时间,灵活性高。
缺点:
1.由于JavaScript是单线程执行的,如果setInterval中的代码执行时间过长,会影响后续代码的执行。
2.如果设置的间隔时间不准确,可能会导致代码执行不稳定。
3.如果需要停止定时器,需要手动调用clearInterval函数。
2.5 示例代码
// 每隔1秒钟输出一次Hello World!
setInterval(function(){
console.log('Hello World!');
}, 1000);
// 每隔1秒钟执行一次动画效果
var box = document.getElementById('box');
var width = 100;
setInterval(function(){
width += 10;
box.style.width = width + 'px';
}, 1000);
// 每隔1秒钟检查一次某个状态
setInterval(function(){
if (document.readyState === 'complete') {
console.log('页面加载完成!');
}
}, 1000);
三、requestAnimationFrame定时器
3.1 定义
requestAnimationFrame是JavaScript中的一个定时器函数,用于在下一次浏览器重绘之前执行指定的函数或代码块。
3.2 语法
requestAnimationFrame(callback)
参数说明:
callback:必需,要执行的函数或代码块。
3.3 使用场景
requestAnimationFrame适用于需要在下一次浏览器重绘之前执行某些操作的场景,比如:
1.实现动画效果。
2.实现游戏循环。
3.实现页面滚动效果。
3.4 优缺点
优点:
1.由于requestAnimationFrame是在下一次浏览器重绘之前执行,可以避免由于代码执行时间过长导致的卡顿现象。
2.可以实现更加流畅的动画效果。
缺点:
1.不支持IE9及以下版本浏览器。
2.由于requestAnimationFrame是在下一次浏览器重绘之前执行,如果需要立即执行某个操作,需要手动调用setTimeout或setInterval函数。
3.5 示例代码
// 实现动画效果
var box = document.getElementById('box');
var width = 100;
function animate() {
width += 10;
box.style.width = width + 'px';
if (width < 200) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
// 实现游戏循环
function gameLoop() {
// 更新游戏状态
// 绘制游戏画面
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
// 实现页面滚动效果
var scrollTop = 0;
function scroll() {
scrollTop += 10;
window.scrollTo(0, scrollTop);
if (scrollTop < 1000) {
requestAnimationFrame(scroll);
}
}
requestAnimationFrame(scroll);
四、个人测试结果 1、使用requestAnimationFrame和时间戳去模拟setInterval的方案,误差大概是一帧时间(16ms)
2、使用setTimout和时间戳的方案,也存在误差; 而且setState异步,react更新也会有耗时,还是不能做到精准秒级更新时间;
3、也看了下web worker的方案,在Can I use看着兼容性一般,不适合前段同学使用;
综合分析下来,在我所需要的计时器场景中,还是原生的setInterval是使用最为广泛的,误差也在可接受范围内的方案,测试的时候我自己mock的情况开了个页面空置几个小时,看起来没有异常,只需要保证单独模块内只有一个计时器就不会出错,并且要合理使用clearInterval();
五、写在最后
最后,这就是我的计时器最终采取的方案啦~
useEffect(() => {
const interval = setInterval(() => {
setCurrentTime(currentTime => currentTime + 1000)
}, 1000);
return () => clearInterval(interval);
}, [startTime]);
const formatTime = (time: number): string => {
if (time < 60) {
return `${time}秒`;
} else if (time < 3600) {
const minutes = Math.floor(time / 60);
const seconds = time % 60;
if(seconds === 0) {
return `${minutes}分`
}
return `${minutes}分${seconds}秒`;
} else if (time < 86400) {
const hours = Math.floor(time / 3600);
return `${hours}小时`;
} else if (time < 864000) {
const days = Math.floor(time / 86400);
return `${days}天`;
}
return "十天以上";
};
return (
<div className='timer'>{formatTime(Math.floor(currentTime / 1000))}</div>
)