你会用 setInterval, setTimeout 互相实现吗?

2,654 阅读2分钟

前言

相信大家对 setInterval, setTimeout 两个 api 是熟悉得不能再熟悉了,它们是我们常用的 web 定时器。

const timer1 = setInteral(() => {
    console.log(1)
}, 1000)

const timer1 = setTimeout(() => {
    console.log(2)
}, 2000)

值得注意的是我们在创建的定时器不用之后最好清除掉。

clearInterval(timer1)

clearTimeout(timer2)

🦃️实现

那我们今天来讨论一下,如何实现一个定时器呢?

关于 setTimeout, 同事给出了这样的实现:

逻辑也非常清晰,在函数内部构造一个循环堵住程序,等到时间到后跳出循环执行 callback,相当于开闸泄洪。

但是这样写肯定是有问题的,我们知道 js 的定时器是定义为宏任务的,在时间循环中是不会阻塞主线程运行的,因此这种是比较 hack 的做法,没有实现宏任务。因此这种是最先废弃的

如果可以用 setInterval,setTimeout 相互实现的话,那我们就有很多思路了

🔥setTimeout

我的 setTimeout 是这样实现的,通过 setInterval 构造一个定时器,到第一次执行时清除定时器,执行回调函数

🔥setInterval

这里构造一个自执行函数,通过 setTimeout 执行完成之后清除定时器,再递归执行,达到仿真 setInterval 的效果

我们看一下效果

执行结果如下:

我们看到这里的定时器是没有阻碍主执行粘的,基本达到了我们的目的

扩展

当我们实现了定时器之后,还有一个问题,我们的定时器是无法消除的,为了可以有添加定时器、清除定时器、清除全部定时器等操作,我决定通过 es6 的类来实现

思路中的大致结构是这样,我们维护一个定时器栈,这个类有 add,run,clear,clearAll 等方法,可以添加,清除,运行定时器,有了这个思路之后我们一步一步来实现

添加定时器

原理很简单,添加定时器就是直接把数据推入我们的栈中,推入前需要做一下简单的数据校验

运行定时器

通过 name 在栈中查找,找到对应的定时器之后执行,这里需要用到 setTimeout 来实现 setInterval,执行方式和上面一样。

清除定时器

原理就是找到对应的定时器,在栈中删掉,这样在 run 的递归函数中找不到对应的定时器就无法执行。清除全部定时器直接清空栈就好。

到这里我们实现了一个简单的 setInterval

我们来看一下效果:

可以看到 4.5s 之后定时器被清除了

到这里我们就实现了一个简单的定时器,你可以写一个 自己的 setTimeout 吗?

关注 前端 100 问,一起学习,持续扩展中