文章目录
- 前言
- 一、setInterval存在的问题
- 1.问题复现
- 2.问题分析
- 二、setInterval缺点及setTimeout
- 1.setInterval缺点
- 2.为什么setTimeout能取代
- setTimeout实现setInterval
- 总结
前言
大家都知道,setTimeout是延迟执行函数,而setInterval就像一个定时器,每间隔一定时间就触发一次回调。
今日面试中,面试官问了我一个问题,为什么要用setTimeout去实现setInterval呢?
再一次被自己菜哭,面试完成后,赶紧去查了查为什么,下面让我们一起看一看setInterval会导致些什么问题吧!
一、setInterval存在的问题
1.问题复现
让我们先来看下面这一串代码:
let startTime = new Date().getTime();
let count = 0;
setInterval(function () {
count++;
console.log(
`与原设定的间隔时差了${
new Date().getTime() - (startTime + count * 1000)
}毫秒`
);
}, 1000);
在执行中可能会造成一点时间误差,但是可以忽略不计。
我们把代码改一改:
let startTime = new Date().getTime();
let count = 0;
//耗时任务
setInterval(function () {
let i = 0;
while (i++ < 1000000000);
}, 0);
setInterval(function () {
count++;
console.log(
`与原设定的间隔时差了${
new Date().getTime() - (startTime + count * 1000)
}
毫秒`
);
}, 1000);
我们在打印之前,增加一个setInterval,来看一看有什么影响
我们可以看到,相差的毫秒数变得非常大并且还在不停的增加!(网页一打开,电脑风扇都开始疯狂运动了!)
结案
:setInterval一定有问题!
2.问题分析
setInterval(fn,time)
上面这串代码的意思是:fn
在每隔time
毫秒之后被推入任务队列
注意
:不是直接放入任务队列
在 setInterval
被推入任务队列时,如果在它前面有很多任务或者某个任务等待时间
较长比如网络请求等,那么这个定时器的执行时间和我们预定它执行的时间可能并不一致。
意味着,如果我们定时器里面的代码需要进行大量的计算,花费的时间就会比较长,那么我上一次的代码还没有执行完毕,那么我又推入了下一次的代码去任务队列,这个时候就会变得不准确。
二、setInterval缺点及setTimeout
1.setInterval缺点
根据上文我们得知,setInterval是存在以下缺点的:
- 使用
setInterval
时,某些间隔会被跳过(如果上一次执行代码没有执行,那么这次的执行代码将不会被放入队列,会被跳过) - 可能多个定时器会连续执行(上一次代码在队列中等待还没有开始执行,然后定时器又添加第二次代码,第一次代码等待时间和执行时间刚好等于第二次代码执行)
通俗来说:每个 setTimeout
产生的任务会直接 push
到任务队列中;而 setInterval
在每次把任务 push 到任务队列前
,都要进行一下判断
(看上次
的任务是否仍在队列中,如果有则不添加,没有则添加)。
2.为什么setTimeout能取代
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
我们看一下这串代码,大家都知道,for里面的宏任务是会被推迟到for循环完成后执行的。(什么?你不知道?赶紧恶补时间队列知识)
广告
:想让for循环里面的异步操作同步执行该怎么做呢?传送门:浅谈Generator和yield
我们打印结果,发现5个结果是同时出来的,这意味着什么?
没错,多个setTimeout并不会互相影响!
所以,我们利用setTimeout模拟setInterval,就能解决掉这个问题!
setTimeout实现setInterval
const myInterval = (fn, time) => {
const timeout = () => {
setTimeout(() => {
fn();
timeout();
}, time);
};
timeout();
};
const test = () => {
console.log("111");
};
myInterval(test, 1000);
成功啦!