聊一聊setTimeout() 和 setInterval()

100 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

  • 请讲一讲setTimeout() 和 setInterval() ?
  • 那不是张口就来。setTimeout表示推迟一段时间延迟执行。setInterval则表示间隔一段时间重复执行。
  • 那你能说说setInterval的缺点吗?你能说说如何使用setTimeout实现setInterval吗?你知道为什么要用setTimeout实现setInterval吗?

我突然一脸懵逼。。。。

那我们今天就来深入研究一下setTimeout() 和 setInterval()。

setInterval的缺点

其实是因为javascript是单线程环境,也就是说定时器仅仅是计划在某个时间段执行,但实际并不能保证执行的时机。

我们有如下需求:假定每隔100ms执行某段代码。

那么我们针对这一需求,肯定会优先选择setInterval实现。

考虑极端情况,假定定时器的代码中需要进行大量的计算,会花费比较长的时间,那么就存在前一次代码还没有执行完成,后一次代码就被添加到队列里了。

如下图所示,假设时间间隔为100ms,需要执行代码的时间为300ms,如图所示:

image.png

简单分析一下:100ms时,队列空闲,代码进入队列,定时器内的代码会被执行。200ms时,第一次定时器中的代码仍在执行,第二次定时器的代码被推入事件队列,等待执行。300ms时,因为第二次的定时器的代码仍在队列中等待,因此这一次代码不会被推入队列中。400ms时,第一次定时器的代码执行完成,队列空闲,第二次定时器的代码开始执行。

因此我们可以看到setIntreval 实现不了需求,并没有达到定时器的效果。可以总结为:1.使用setInterval时,某些间隔会被跳过;即使setInterval调用的方法报错了,他仍然会继续执行。 2.无视网络延迟,可能多个定时器会连续执行。

因此可以这么理解:每个setTimeout产生的任务会直接push到任务队列中;而setInterval在每次把任务push到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中)。

因此我们一般用 setTimeout 模拟 setInterval,来规避掉上面的缺点。

用 setTimeout 模拟 setInterval

const repeat = (func, ms) => {
  setTimeout(() => {
    func()
    repeat(func, ms)
  }, ms)
}