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

来源:互联网 发布:arpu值算法 编辑:程序博客网 时间:2024/05/23 05:08

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

/* *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开始无上限递增(如果定时器足够的话), 至于顺序则是按照定时器生成的时间顺序.

原创粉丝点击