使用SetTimeout模拟/模仿SetInterVal(JavaScript)

问题背景

  近日我在开发一个答题小程序的时候,需要判断用户在规定答题时间内完成所有答题。但是,遇到了一个问题是使用setInterval(func,time)的时候。担心会遇到一些问题。造成这个担心的原因是因为之前开发VUE单页应用的时候.从别的选项卡切换当前网页选项卡的时候.当前网页的倒计时会一度变得不如预期所想的那样(1s变一下数字)。而是倒计时的速度变得非常快。
  于是,网上查到了如何使用setTimeOut()来模拟setInterVal()

代码

     var timer;
     var i = 1;
     timer = function () {
         i++;
         console.log(i);
         if (i == 10) {
             timer = function () {
                 console.log("终止运行");
             }
         }
         setTimeout(timer, 500);
     };
     console.log(timer);
     setTimeout(timer, 500);

  刚开始理解这段代码的时候,我有点不知所以然.但是现在觉得是非常容易理解的.在这段函数是timer指向了一个内存空间的对象的引用(这个对象可以执行一些动作,它就是函数).
setTimeout会在倒计时结束之后去回调这个函数.而在回调这个函数到函数执行体内的时候,又会去设置一个定时器.这个定时器的回调函数指向了一个内存空间的引用.这还是Timer.所以它会起到模拟setInterval的效果.
  同时,这种写法也是更为可控的.
  如何取消这个定时器.只需要把timer设置为别的值,或者指向别的引用就可以了.

 timer = function () {
    console.log("终止运行");
    }

以下是一种函数的写法

let timer = null
interval(func, wait){
    let interv = function(){
        func.call(null);
        timer=setTimeout(interv, wait);
    };
    timer= setTimeout(interv, wait);
 },

interval(function() {}, 20);

在VUE里,只需要将timer改为类似于this.timer就可以了.

需要注意的是定时器并不是严格按照预期时间去执行的.它可能提前或者延迟执行.这和Javascript的语言特性有关.众所周知JavaScript是单线程语言.JavaScript的多线程和异步是依靠于EventLoop(事件循环)机制来进行实现的.语言特性决定了定时器总是趋近于设定时间去执行的.

      setTimeout(function () {
            console.log("等等我呀");
            console.timeEnd('caltime');
        }, 1000);
        console.log('我先执行咯');
        console.time('caltime');


        // 以下代码仅用于消耗调用栈的执行时间
        var speedTime = [];
        for (var i = 0; i < 100000000; i++) {
            speedTime.push(i);
        }

以上代码经过实际测试会发现,定时器的调用间隔不是1s.我这里经过测试是2s左右.这个与电脑硬件有关系.所以使用定时器来达到一些精准的任务并不是一个最好的选择.



参考

上一篇:Pass parameter to setTimeout callback function


下一篇:SetTimeout(延迟计时器)