setInterval 倒计时陷阱

最近发现页面上的一个倒计时功能出现了比较大的误差,debug 之后发现误差是由于 setInterval 不能实现准确执行任务导致的,例如下面这个例子:

1
2
3
4
5
    var now = +new Date();
    setInterval(function () {
        now += 100;
        console.log('diff', now - (+Date.now()));
    }, 100);
















执行次数: 0
累计误差(ms): 0
平均误差(ms): 0


在我的 chrome 浏览器上平均每次执行时会有 1ms 的延迟。由于这个延迟是累加的,当代码执行 1000 次时误差就积累到了 1 秒。

想到的解决方法就是在执行过程中不停的修正下一次运行函数的时间,由于setInterval 的间隔时间是在创建 timer 时指定的,所以就只能用 setTimeout 来实现定时器。

1
2
3
4
5
6
7
8
    var now = +new Date();
    setTimeout(function run() {
        now += 100;
        var fix = now - (+Date.now());
        console.log('diff:', fix)

        setTimeout(run, 100 + fix);
    }, 100);
















执行次数: 0
误差(ms): 0
平均每次执行误差(ms): 0


setTimeout 就解决了 setInterval 误差累计的问题。

简单封装一下:

1
2
3
4
5
6
7
8
9
10
11
12
    function accurateInterval(callback, interval) {
        var now = +new Date();

        setTimeout(function run() {
            now += interval;
            var fix = now - (+Date.now());

            setTimeout(run, interval + fix);

            callback();
        }, interval);
    }

末尾,抛几块砖

  • accurateinterval 函数中,是否可以先执行 callback 函数,再 setTimeout
  • callback 函数的执行时间超过定时间隔后会出现什么事情?
  • accurateinterval 实现的倒计时,会出现走时不均匀的问题。也就是说倒计时的数字会时快时慢,如何解决这个问题?

- FIN -