需求
倒计时这种需求非常常见。在我接触的项目中,已经做过N个倒计时的需求。常见的场景有电商项目中的秒杀抢购活动倒计时,短信验证码等。
现在再一次碰到了倒计时的需求,是一个答题倒计时的场景。具体效果如图。
思路
要实现倒计时,就需要来回顾一下JavaScript中的定时器相关知识。
setInterval: 每间隔N秒执行一次回调函数。
setTimeout: N秒后执行回调函数。
setImmediate: 非标准的API,目前尚未被正式采纳,用于执行耗时的运算。执行完其它代码,就会立即执行。
requestAnimationFrame: 类似于setInterval,用于动画。采用系统时间,保证时间的准确性,但无法指定间隔时间。
从上面可以看出,能指定某个时间触发的定时器,仅有setInterval和setTimeout,这种场景下,我选择setInterval。当然也可以对setTimeout进行递归,不断重新创建和销毁的方式,达到同样的效果。
首先我们明白,因为JavaScript是单线程的,在事件循环过程中,当前宏观任务队列中的微观任务会阻塞下一个宏观任务队列中任务的执行。所以会造成一种现象,定时器中的真实执行时间并不会精准的按照第2个参数所设定的数值执行。比如设置1000毫秒,如果到了1000毫秒,主线程被其他任务所占用了,那么就会等待其它任务的执行,等其它任务执行完毕后,才会执行定时器的回调函数。
也就是说,如下代码代表的意思不是1秒后执行,而是最快1秒后执行。
setTimeout(() => {console.log('我是定时器!')},1000);
复制代码
你可以尝试执行如下代码,会发现定时器的执行时间应该超过了1秒钟,如果正常执行,你可以从循环条件后面加个0。电脑配置很差的就不要试了。
setTimeout(() => {console.log('我是定时器!')}, 1000);
for (let i = 0; i<1000000000; i++) {}
复制代码
碰到这种循环或者递归代码时,回调函数的执行时间会根据不同的电脑运算速度决定。如果你的电脑配置够强,比如小型机,高性能服务器等,能够在1秒以内执行完逻辑,那么就不会影响定时器的正常执行。
要想做到时间相对准确,就必须解决这个问题,