js設置定時器和清除定時器


一、前言
  在前端,我們有很多功能需要用到定時器。譬如輪詢,譬如定時關閉彈框,譬如實現秒表,譬如一段時間后跳轉頁面等等。因此,我們需要掌握定時器的用法。
二、設置定時器
  目前window對象提供有兩個方法來實現定時器的效果,分別是window.setTimeout()和window.setInterval()。
  其中setInterval()的作用是:使一段代碼每過指定時間就運行一次;常用於輪詢。

setInterval(function(){
	console.log("這是一個setInterval定時器!");
}, 1000);//1000ms=1s,設定的代碼循環運行時的間隔時間,單位為ms

  而setTimeout()的作用是:使一段代碼在指定時間后運行;常用於一次性定時器及輪詢。注意,setTimeout()是只運行一次代碼的,若要用setTimeout()進行輪詢,需要在setTimeout()的代碼里再調起當前setTimeout()所屬函數,才能達到循環的效果。

setTimeout(function(){
	console.log("這是一個setTimeout定時器!");
}, 1000);//1000ms=1s,設定的代碼等待運行的時間,單位為ms

三、定時器的缺陷
  1.定時器最大的優點,同時也是它最容易出錯的一點:定時器所有任務都是由同一個線程來調度。注意:定時器是異步執行的,它會放到所有同步任務都執行完之后再開始執行。
  正是因此,定時器簡單易用。同時這也導致了定時器所有任務都是串行執行的,同一時間只能有一個任務在執行,前一個任務的延遲或異常都將會影響到之后的任務,即定時器的重疊。
  2.定時器不會被自動銷毀,即它所占內存無法被自動回收。如果不手動清除定時器,它會一直占用內存資源。更可怕的是,一旦使用定時器進行輪詢,定時器所占的內存資源將會不斷上升,若與定時器重疊問題一起出現,常導致頁面卡頓。
  所以,我們必須學會清除定時器。
四、清除定時器
  定時器在調用時,都會返回一個整型的數字,該數字代表定時器的序號,即第多少個定時器,我們可以利用定時器的序號對定時器進行清除。
  因此在清除定時器時,我們常在設置定時器時,定義一個變量來記錄定時器返回的定時器序號,然后在定時器完成后,調用該序號清除對應定時器。
  目前有兩個方法來清除定時器,分別對應兩種定時器的方法。
  其一是clearInterval(obj),對應setInterval()定時器;

var intervalBox = setInterval(function(){
    console.log(intervalBox);//打印interval定時器,查看interval定時器效果
}, 1000);
clearInterval(intervalBox);//清除interval定時器

  其二是clearTimeout(obj),對應setTimeout()定時器;

var timeoutBox = setTimeout(function(){
    console.log(timeoutBox);//打印interval定時器,查看interval定時器效果
}, 1000);
clearTimeout(timeoutBox);//清除timeout定時器

  注:事實上,我們在使用定時器時,常遇到這樣的情況:頁面跳轉時,上一個頁面的定時器未被清除,重新回到該頁面時,觸發定時器,造成定時器重疊。

  要解決這個問題,需要分情況。若你的項目使用了vue或react等框架,頁面存在鈎子函數的話,在銷毀頁面的鈎子里清除定時器即可。若是單頁面,沒有鈎子函數,可以在定時器中去判斷該頁面的dom元素是否存在來確定是清除定時器還是繼續跑定時器:

<div id="pageId"></div>
<script type="text/javascript">
    var timeBox;
    function test() {
        clearTimeout(timeBox);
        if(document.getElementById('pageId') === null){
            return;
        }
        timeBox = setTimeout(()=>{
            console.log(timeBox);
            test();
        },1000);
    };
    test();
</script>

五、常用setTimeout()代替setInterval()定時器的原因

  setTimeout()只執行一次,而setInterval()會循環調用。從理論上來說,我們應該使用setInterval()來執行輪詢,但實際上,我們會用setTimeout()調用自身實現循環來代替setInterval(),原因很簡單,setInterval()存在兩個重大缺陷:

  一、setInterval()無視代碼錯誤
  與setTimeout()不同,setInterval()中調用的代碼報錯並不會阻塞setInterval()的循環。如果setInterval()執行的代碼由於某種原因出了錯,它依然會繼續循環調用該代碼。

  二、setInterval()無視內部代碼延遲
  不管setInterval()內部的代碼需要多久才能完成,哪怕這段代碼沒有運行完成,setInterval()都會按照設置的時間不斷循環該段代碼。這就導致setInterval()內部的代碼執行不准確或者執行出錯。
  例如,若使用setInterval()執行ajax輪詢,遇到服務器過載、網絡差等原因,ajax請求時間過長,到了setInterval()設置的時間,請求依舊未完成,setInterval()依然會發出一個新的ajax請求,最后,你的客戶端網絡隊列會塞滿ajax請求。

  因此,我們常用setTimeout()調用自身實現循環來代替setInterval()。

六、總結示例
  為實現相應功能,定時器不可或缺性,但是若不規范使用定時器,將會造成巨大困擾,輕則內存資源被嚴重占用,頁面卡頓,重則邏輯斷裂,出現重大bug。因此我們需要對定時器規范使用,及時清除。
  我對定時器的使用有八字總結:隨意使用,即時清除。
  
簡單來說,每設置一個定時器,都要對應清除,示例代碼如下:

<html>
<head>
    <meta charset="utf-8">
    <title>定時器</title>
</head>
<body>
<button onclick="startInterval()">開始Interval</button>
<button onclick="stopInterval()">停止Interval</button>
<button onclick="startTimeout()">正常Timeout定時器</button>
<button onclick="startTimeoutTwo()">循環的Timeout定時器</button>
<button onclick="stopTimeout()">停止Timeout</button>
<script>
    var intervalBox;//interval定時器存儲器
    //設置interval定時器
    function startInterval() {
        clearInterval(intervalBox);//初始化interval定時器,防止定時器重疊
        intervalBox = setInterval(function(){//設置interval定時器
            console.log(intervalBox);//打印interval定時器,查看interval定時器效果
        }, 1000);//定時器間隔時間1000ms
    }
    //結束interval定時器
    function stopInterval() {
        clearInterval(intervalBox);//清除interval定時器
    }

    var timeoutBox;//timeout定時器存儲器
    //設置正常的timeout定時器
    function startTimeout() {
        clearTimeout(timeoutBox);//初始化timeout定時器,防止定時器重疊
        timeoutBox = setTimeout(function(){//設置timeout定時器
            console.log(timeoutBox);//打印timeout定時器,查看timeout定時器效果
            clearTimeout(timeoutBox);//清除當前timeout定時器,timeout定時器只運行一次代碼,直接清掉它
            // location.href="timer.html";//一段時間后跳轉頁面是setTimeout的常用場景之一
        }, 1000);
    }
    //設置循環的timeout定時器
    function startTimeoutTwo() {
        clearTimeout(timeoutBox);//初始化timeout定時器,防止定時器重疊
        timeoutBox = setTimeout(function(){
            console.log(timeoutBox);//打印timeout定時器,查看timeout定時器效果
            startTimeoutTwo();//循環調用函數自身,以達到循環的效果
        }, 1000);
    }
    // 結束循環的timeout定時器
    function stopTimeout() {
        clearTimeout(timeoutBox);
    }
</script>
</body>
</html>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM