之前印象中一直记得setInterval有一些坑,但是一直不是很清楚那些坑是什么。今天去摸索了下之后,决定来做个记录以免自己忘记,也希望让更多人了解到这个坑。
其实解决方案也很简单,就是使用setTimeout,然后再setTimeout里递归调用。
比如说第一个和第二个坑就可以这样写:
function fn () {
setTimeout(() => {
// 程序主逻辑代码
// 循环递归调用
fn();
}, 1000);
}
fn();
可是使用setTimeout后,我们又可能会遇到一个问题,就是计时器的下次触发时间是在当前的触发时间上开始计算的。这对于第二个坑这种情况是合理的,可是有时候我们又希望它能“匀速”地被触发。也就是说,希望计时器的触发时间尽可能在计时器注册时间+周期*delay附近。这个时候,我们就可以用预期下次发生的时间减去当前的时间来得到一个精确的delayTime。
我写了一个简单的函数来实现这一点:一开始调用该函数的时候,会记录当前的计时器注册时间,以及一个用来统计计算器调用次数的变量。之后在每次调用newFn的时候,都会使用预期下次发生的时间减去当前的时间来得到一个精确的delayTime。这样至少可以保证在一些情况下,计时器可以稍微精确的执行。
function accurateTimers (fn, expectDelayTime) {
let init = false;
let registDate = new Date(); // 计时器注册时间
let count = 0; // 计时器调用次数
function newFn() {
let delayTime;
count++;
if (!init) {
init = true;
delayTime = expectDelayTime;
} else {
delayTime = expectDelayTime * count + registDate.getTime() - new Date().getTime();
}
console.log(delayTime);
setTimeout(() => {
fn();
newFn();
}, delayTime);
}
newFn();
}
accurateTimers(function () {
let startDate = new Date();
// 延迟500ms
while (startDate.getTime() + 500 > (new Date()).getTime()) {
}
}, 1000);
以上,就是本次文章的内容。这篇文章只是做一个简单的记录,希望能帮大家了解到setInterval的坑的地方,在实际编程中可以少走点弯路。如果觉得有用的话,欢迎点个赞或者关注哦。谢谢。