「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
关于setTimeout和setInterval
倒计时误差产生原因:
setInterval: 每隔n秒回调一次函数,间隔最小是n秒,要是回调函数里面有复杂功能执行,间隔会远大于秒, 还有就是页面休眠时,定时器就会停止setTimeout: 回调函数在n秒之后执行。- javascript单线程运行机制,这2个方法都会被线程
阻塞。 react和vue中,diff算法和DOM渲染都在一个主线程中执行。
解决的方案
- 开启一个后台线程单独运行定时器
web worker - 抹平误差值
requestAnimationFrame
javascript是单线程执行的,所以以上这2个方法都是会被线程阻塞,比如setInterval延迟设置为1000,如果内部的执行是一个耗时超过1秒的操作,那么每次重复执行的时候会造成2个问题:
-
- 执行被阻塞,预期是1000毫秒执行一次,实际上必须在阻塞结束后才执行。
-
- 阻塞时,setInterval回调会被堆积,当阻塞结束后,堆积只会被消费一个,更可怕的是
etInterval在被阻塞一次后,后面的所有执行时间间隔都会被打乱,如果被阻塞N次,时间间隔就会越来越乱
- 阻塞时,setInterval回调会被堆积,当阻塞结束后,堆积只会被消费一个,更可怕的是
const before = Date.now();
let after = Date.now();
setTimeout(() => {
after = Date.now();
console.log('时间差值===>', after - before);
}, 1000);
for (let i = 0; i < 1000000000; i++) {}
// 执行三次的结果:
// 时间差值===> 1130
// 时间差值===> 1026
// 时间差值===> 1028
如何解决堆积问题呢?
因为阻塞是不可能被解决的,那么最简单的方法就是把setInterval换成setTimeout,使用一个递归来造成每隔多久执行一次的功能。当然阻塞是无法被解决的,这里的阻塞不仅仅有回调中的,还有浏览器中的方方面面的阻塞,比如用户的一些操作行为,其他定时器等外部的阻塞,所以这也就是无论我们如何做,页面开久了,定时器都会不准,或者说,变慢的根本原因。
Web Worker的使用
常规方式
index.htnl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>倒计时</title>
</head>
<body>
<div>
<div class='root'>
距离2022年8月9日:
<span class="daySpan"></span>
<span class="hourSpan"></span>
<span class="minuteSpan"></span>
<span class="secondSpan"></span>
</div>
</div>
</body>
<script src = './countDown.js'></script>
</html>
countDown.js
const daySpan = document.querySelector(".daySpan"),
hourSpan = document.querySelector(".hourSpan"),
minuteSpan = document.querySelector(".minuteSpan"),
srcondSpan = document.querySelector(".secondSpan");
deadLine = new Date("2022-8-9 00:00"); //未来要开始的时间
function countDown() {
const now = new Date();
timeRemainning = deadLine - now; // 剩余倒计时
let day, hour, minute, second;
if (timeRemainning < 0) {
return 0;
}
second = Math.floor((timeRemainning / 1000) % 60);
minute = Math.floor((timeRemainning / 1000 / 60) % 60);
hour = Math.floor((timeRemainning / 1000 / 60 / 60) % 24);
day = Math.floor(timeRemainning / 1000 / 60 / 60 / 24);
daySpan.innerHTML = day + "天";
hourSpan.innerHTML = hour + "时";
minuteSpan.innerHTML = minute + "分";
srcondSpan.innerHTML = second + "秒";
setTimeout(countDown, 1000);
}
countDown();