循环定时器--用setTimeout代替setInterval

本文深入探讨了循环定时器(setInterval)的工作原理及其潜在问题,包括间隔时间的不准确性及定时器任务的执行顺序问题,并提供了一种使用链式调用setTimeout作为setInterval替代方案的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

续前缘 
循环定时器的写法, 很多人应该熟悉

/*
 *func 回调
 *interval 间隔时间
*/
setInterval(func, interval)

大部分人(包括之前的我)都将循环定时器理解为: 每间隔一段时间就执行一次回调. 其实这种说法并不准确. 如果强行这么理解, 那就要加上两个条件: JS进程永远处于空闲状态; 回调函数执行时间小于间隔时间.
可以做个试验:

setInterval(function(){
        var script = document.createElement("script")
        script.src = "http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"
        script.onload = script.onreadystatechange = function () {
            if (!script.readyState || 'loaded' === script.readyState || 'complete' === script.readyState) {
                console.log(new Date().getTime())
            }
        };
        document.querySelector("body").appendChild(script)
    }, 1000)

结果如下所示:
这里写图片描述
这串数据前后相隔时间虽然在1000上下徘徊, 但都不精确.
区分两件事情: JS进程和JS队列时间线是并行处理的; 同一时间, 定时器在队列中的回调函数只能有一个.
先说下间隔小于1000的情况: 假设间隔时间为t1, 代码执行时间为t2, 且t2>2t1, 此时定时器代码会跳过间隔时间且连续运行定时器代码. 直观呈现就是abs(time1-time2)<1000.
再说下间隔大于1000: 当t2<2t1 && t2>t1 这属于正常情况
综上, 循环定时器是有问题的:
1. 某些间隔会被跳过
2. 多个定时器的代码执行时间可能会比预期的小

为了避免这两个问题的出现, setInterval可以采用链式调用setTimeout代替.

function Interval(){
    this._interval_flag = null //初始化: 定时器在队列中的顺序
}
Interval.prototype = {
    createIns: function(fun, interval){ //创建定时器
        var that = this //防止this指向发生变化
        var fouth = setTimeout(function(){
            if(typeof(fun) == "function"){
                fun()
            }else{
                return console.error("Type of the argument \"fun\" is not \"function\"")
            }
            that._interval_flag = setTimeout(arguments.callee, interval)
        }, interval) //链式调用setTimeout
        console.log("fouth:"+fouth)
    }
    ,clearIns: function(){ //清除定时器
        clearTimeout(this._interval_flag)
    }
}
var subInterval = new Interval() //实例化
subInterval.createIns(function(){
    var script = document.createElement("script")
    script.src = "http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"
    script.onload = script.onreadystatechange = function () {
        if (!script.readyState || 'loaded' === script.readyState || 'complete' === script.readyState) {
            console.log(new Date().getTime())
        }
    };
    document.querySelector("body").appendChild(script)
}, 1000)

如上所述, 链式调用setTimeout替代setInterval, 可以保证: 在前一个定时器代码执行完之前, 不会像队列中插入新的定时器代码, 从而确保不会有任何确实的间隔; 此外, 还可以保证在下一次定时器代码执行之前, 至少要等待指定时长的间隔, 避免定时器代码连续运行.
有人可能会想, 多个定时器之间, 它们的顺序是怎样的. 其实定时器给自己开辟了一块内存来存放索引(只存放定时器: 包括单次和循环), 索引值从1开始无上限递增(如果定时器足够的话), 至于顺序则是按照定时器生成的时间顺序.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值