美文网首页web前端
setTimeout代替setInterval的方案

setTimeout代替setInterval的方案

作者: 姜治宇 | 来源:发表于2020-03-29 17:50 被阅读0次

    生产环境下, 我们一般会使用setTimeout代替setInterval,为啥呢?
    setInterval其实就是每隔一段时间,往任务队列推入一个回调函数。假如setInterval的回调需要进行大量的dom操作,这样一来,每次任务花的时间就比较长,由于设定了间隔时间,有可能前一次代码还没有执行完,后一次代码就被添加到任务队列了。随着等待的任务不断增多,这样一旦JS引擎空闲后一个任务会马上得到执行,这样慢慢就变成了连续执行回调,设置的时间间隙就失去了作用。如何解决呢?
    我们可以用setTimeout递归调用的思路来代替setInterval。

    function mysetInterval(){
        let fn = null
        let isClear = arguments[0]//是否清除定时器
        let callee = arguments.callee //函数自身引用
        if(typeof isClear === 'boolean'){
    
            fn = arguments[1]
            fn.timer && clearTimeout(fn.timer)//清除定时器,回调的callee不会再执行
    
        } else {
            fn = arguments[0]  //回调函数
            let wait = arguments[1]  //间隔时间
            let that = this
    
            fn.timer = setTimeout(function(){
    
                fn.call(that)
                callee(fn,wait)//回调函数中递归调用自身
            },wait)
        }
    
    
    }
    //测试一下
    function foo(){
        console.log(new Date())
    }
    mysetInterval(foo,1000)//1s执行一次
    
    //5s后停止
    setTimeout(function(){
        mysetInterval(true,foo)
    },5000)
    

    问题的关键,就在setTimeout的回调函数中callee调用自身。
    我们推演一下执行顺序。
    当mysetInterval函数执行后,代码走到else并建立一个定时器,此时定时器线程拿到了timeout开始执行,经过1s后,将回调函数推入宏任务队列,定时器线程空闲,目前宏任务队列只有一个回调函数待执行。
    等JS引擎空闲后,执行栈从宏任务队列拿到这一个回调函数并开始执行,执行完毕后,再次建立一个timeout。定时器再次拿到timeout开始执行,1s后将回调函数推入宏任务队列,以此类推。。。。
    这样做的好处是可以保证每次消息队列只有一个回调函数待执行,而不是setInterval的多个。

    相关文章

      网友评论

        本文标题:setTimeout代替setInterval的方案

        本文链接:https://www.haomeiwen.com/subject/rgiyuhtx.html