最近在做專案中遇到了一個關於setInterval的問題。需求是能通過改變setInterval中的間隔時間,實現快進2倍、快進4倍 的功能,當快進速度超過4倍時,播放速度恢復到正常速度。可是但我點選三次過後,發現速度並沒有恢復到正常速度,速度反而是更快了。一開始沒多想,就在每次點選之後呼叫clearInterval(timer)方法,發現問題解決了,但是我並不知道為什麼這樣做能夠解決這個問題。後來通過查閱關於setTimeout和setInterval的相關資料,寫出了這篇文章,來跟大家一起探討一下。
定時器的種類
1、setTimeout(code,millisec)
引數 | 描述 |
---|---|
code | 要執行的程式碼 |
millisec | 表示需要等待的時間,表示在millisec毫秒後將code加入到當前執行執行緒中 |
setTimeOut中的第一個引數(code)可以是字串也可以是函式,但是字串可能導致效能損失,所以最好是使用函式。且code只會被執行一次。呼叫cleartimeout()可以終止setTimeOut方法的執行。下面來看幾個例子
document.getElementById("btn").onclick = function() {
var start = new Date().getTime();
setTimeout(function() {
console.log((new Date().getTime() - start) + "毫秒後開始執行setTimeout中的程式碼");
}, 1000);
test();
}
function test() {
var stime = new Date().getTime();
var content = ""
for(var i = 0; i < 5000000; i++) {
content = content + "34636";
}
var etime = new Date().getTime()
console.log("迴圈執行了" + (etime - stime) + "毫秒");
}
複製程式碼
輸出結果為:
document.getElementById("btn").onclick = function() {
var start = new Date().getTime();
setTimeout(function() {
console.log((new Date().getTime() - start) + "毫秒後開始執行setTimeout中的程式碼");
}, 500);
test();
}
function test() {
var stime = new Date().getTime();
var content = ""
for(var i = 0; i < 5000000; i++) {
content = content + "34636";
}
var etime = new Date().getTime()
console.log("迴圈執行了" + (etime - stime) + "毫秒");
}
複製程式碼
輸出結果為:
document.getElementById("btn").onclick = function() {
var start = new Date().getTime();
setTimeout(function() {
console.log((new Date().getTime() - start) + "毫秒後開始執行setTimeout中的程式碼");
}, 0);
test();
console.log("函式執行完畢")
}
function test() {
var stime = new Date().getTime();
var content = ""
for(var i = 0; i < 5000000; i++) {
content = content + "34636";
}
var etime = new Date().getTime()
console.log("迴圈執行了" + (etime - stime) + "毫秒");
}
複製程式碼
輸出結果為:
上面的程式碼表示:當javscript程式碼執行到setTimeout時,500/1000/0毫秒後將setTimeout裡面的函式加入到javascript執行佇列中。如果佇列為空的話,500/1000/0毫秒後將執行setTimeout的函式,如果此時佇列不是空的,那麼就要等前面的程式碼執行完了才執行setTimeout中的函式。總結:從第三段程式碼,setTimeout中的第二個引數為0可以看出setTimeout總是在建立它的函式體執行完畢之後,才會去執行。
如果需要取消超時呼叫的話,需要將setTimeout()賦值給某個變數,然後呼叫clearTimeout()方法,程式碼如下:
var timer = setTimeout(function() {
console.log("我是測試資料");
}, 0);
clearTimeout(timer);
複製程式碼
2、setInterval(code,millisec)
引數 | 描述 |
---|---|
code | 要執行的程式碼 |
millisec | 表示需要等待的時間,表示在millisec毫秒後將code加入到當前執行執行緒中 |
setInterval和setTimeOut的引數意義是一樣的。只不過setTimeout中的code只會被執行一次,而setTimeout中的code會不斷的被執行。呼叫clearInterval()可以終止setInterval方法的執行。這種重複定時器有兩個問題:
- 某些間隔會被跳過;
- 多個定時器的程式碼執行之間的間隔可能比預期的要小。
下面來看一下serInterVal的例子
<button id="btn1">開始</button>
<script type="text/javascript">
window.onload=function(){
var i=1;
document.getElementById("btn").onclick=function(){
console.log("第"+(i++)+"此點選")
setInterval(function(){
console.log(new Date().getSeconds())
},1000)
}
}
</script>
複製程式碼
輸出結果如下:
結果分析:當我點選第一次時,setInterval中的函式體每1秒執行一次,當我點選第二次時,setInterval中的函式體每1/2秒執行一次,當我點選第三次時,setInterval中的函式體每1/3秒執行一次,當我點選第三次時,setInterval中的函式體每1/4秒執行一次。出現這樣的原因是因為點選4次就呼叫了4次serInterval方法,相當於呼叫了4個定時器。用如下的圖來進行分析能很好地解釋為什麼會出現上面情況的原因了。
結果分析:如上圖所示,在0-1s內點選2次,在1-2s內點選第3次,在2-3內點選第4次,那麼在第2秒內會呼叫兩次setInterval中的函式體。在第3秒內會呼叫3次函式體,第4秒內會呼叫第4次函式體。
下面是驗證過程: 程式碼如下:
<button id="btn1">開始</button>
<button id="btn2">結束</button>
<script type="text/javascript">
window.onload = function() {
var i = 1;
document.getElementById("btn1").onclick = function() {
console.log("第" + (i++) + "此點選")
var timer = setInterval(function() {
console.log(new Date().getSeconds())
}, 1000);
document.getElementById("btn2").onclick = function() {
console.log("點選結束按鈕");
clearInterval(timer);
}
};
}
</script>
複製程式碼
執行結果如下:
如上圖所示,點選3次按鈕後,每1秒內呼叫3次setInterval中的函式,但點選結束按鈕後,每1秒內還執行2次setInterval中的函式。執行結果證明我上面的分析時對的。 為了解決上面點選開始按鈕多次會呼叫多個setInterval的情況,我總結了下面這個供大家參考。方法1程式碼如下
<button id="btn1">開始</button>
<button id="btn2">結束</button>
<script type="text/javascript">
window.onload = function() {
var i = 1;
var timer=null;
document.getElementById("btn1").onclick = function() {
if(timer){
clearInterval(timer);
}
console.log("第" + (i++) + "此點選");
timer = setInterval(function() {
console.log(new Date().getSeconds())
}, 1000);
};
document.getElementById("btn2").onclick = function() {
console.log("點選結束按鈕");
clearInterval(timer);
}
}
</script>
複製程式碼
先點選2次開始按鈕,然後再點選結束按鈕,操作輸出結果如下:
總結:呼叫setInterval方法時,要先呼叫clearInterval方法清除setInterval執行後返回的timer。呼叫setTimeout方法也一樣,要先呼叫cleartimeout方法清除setTimeout執行後返回的timer。