續前緣 …
循環定時器的寫法, 很多人應該熟悉
/*
*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開始無上限遞增(如果定時器足夠的話), 至於順序則是按照定時器生成的時間順序.