续前缘 …
循环定时器的写法, 很多人应该熟悉
/*
*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开始无上限递增(如果定时器足够的话), 至于顺序则是按照定时器生成的时间顺序.