setTimeout和setInterval從入門到精通


我們在日常web前端開發中,經常需要用到定時器方法。
前端中的定時器方法是瀏覽器提供的,並不是ECMAScript規范中的。是window對象的方法。

瀏覽器中的定時器有兩種,

  1. 一種是每間隔一定時間執行一次,循環往復。比如每隔一秒執行一次,六十秒過后執行了60次。
  2. 一種是過了一定時間執行一次,只執行一次。比如隔一秒后執行一次,過了十萬八千秒后也只在第一秒執行了一次,僅有的一次。
  1. 第一種是:window.setInterval
  2. 第二種是:window.setTimeout

由於window在瀏覽器中是全局對象,可以省略,所以常用setInterval和setTimeout

這兩個方法的參數是一模一樣的:

  1. 正常使用的話,至少需要有兩個參數。【這一條可以忽略】
  2. 不想出現報錯的話,至少必須得一個參數。
  3. 都可以傳遞無數個參數。
  4. 第一個參數是要執行的js語句,有三種以上的情況
    1. 字符串:"console.log('I am Pelli');"【可讀性差,推薦指數:1/10】
    2. 匿名函數:function(){console.log("I am Pelli")}【可讀性強,推薦指數:8/10】
    3. 函數名:showName【可讀性強,耦合度低,可裝逼,推薦指數:9/10】
    4. 其他亂七八糟的內容【非正常程序員干的事情】
  5. 第二參數是用來描述時間,以毫秒為單位。有一種以上的情況
    1. 數值【正常情況下】
    2. 數值以外的其他數據類型【異類程序員干的事情】

 1 // 第一個參數的第一種情況
 2 var timer1 = setTimeout("console.log('I am Pelli')");
 3 // 第一個參數的第二種情況
 4 var timer2 = setTimeout(function(){
 5     console.log("I am Pelli");
 6 });
 7 // 第一個參數的第三種情況
 8 function showMyName(){
 9     console.log("I am Pelli");
10 }
11 var timer3 = setTimeout(showMyName);
12 // !注意:由於以上三種情況都沒有傳遞控制時間的第二個參數,第二個參數默認是0,都會在0秒后執行。
以上三種方式的效果一模一樣
13 // 第一個參數的第四種情況【實際開發中基本上無用】 14 var timer4_0 = setTimeout(null); 15 var timer4_1 = setTimeout(12); 16 var timer4_2 = setTimeout({});//Uncaught SyntaxError: Unexpected identifier 17 var timer4_3 = setTimeout([]); 18 var timer4_4 = setTimeout(NaN); 19 var timer4_5 = setTimeout(false); 20 var timer4_6 = setTimeout(/I am Pelli/);

綜上所述:第一個參數的數據類型不能是引用類型,對象和數組不能直接傳遞(這和瀏覽器有關,不同的瀏覽器處理的方式不一樣),其他的諸如字符串,數值,boolean,正則等,都可以直接傳遞,不過在實際使用中很少有人這么做,沒什么實際的意義。

接下來的描述,我們的第一個參數都是以第二種為准。

現在我們來看第二個參數

setTimeout和setInterval的第二個參數是用來描述時間的。以毫秒為單位。1s=1000ms[1秒=1000毫秒]
該參數通常都是直接傳遞數值的,比如12,120,1000,2000之類的,數據類型為:“number”

1 // 一秒以后輸出:I am Pelli
2 var timer5 = setTimeout(function(){
3     console.log("I am Pelli");
4 },1000);
5 
6 // 兩秒后輸出:我是沛笠
7 var timer6 = setTimeout(function(){
8     console.log("我是沛笠");
9 },2000);

當然咯,還有一些不正常的程序員會傳遞一些亂七八糟的參數。讓我們來看看不正常的程序員會傳遞哪些參數

第二個參數除了直接傳遞數值外,還可以傳遞非數值類型的值,如果該值能轉換成數值類型,則時間間隔就是轉換后的值,如果不能轉換成數值類型,則時間間隔為默認值。

//第二個參數將會被轉換成0
var timer7_0 = setTimeout("console.log('I am Pelli');","");

//轉換成0
var timer7_1 = setTimeout("console.log('I am Pelli');",false);

//轉換成1
var timer7_2 = setTimeout("console.log('I am Pelli');",true);

//200
var timer7_2 = setTimeout("console.log('I am Pelli');",true + 199);

//不能轉換成數值,默認0
var timer7_3 = setTimeout("console.log('I am Pelli');",NaN);

//轉換成0
var timer7_4 = setTimeout("console.log('I am Pelli');",[]);
var timer7_5 = setTimeout("console.log('I am Pelli');",{});
var timer7_6 = setTimeout("console.log('I am Pelli');",/I am Pelli/);
var timer7_7 = setTimeout("console.log('I am Pelli');",function(){});

可以看到,第二個參數也是可以傳遞各種各樣的參數的,只要是能轉換成數值類型的,最終都會轉換成數值類型,如果沒有轉換成數值類型的,也不會報錯,最終會使用默認時間值。默認值具體是多少,要看是哪種JS引擎。不同的JS引擎賦予的默認值是不一樣的。
不過我們作為一個正常的程序員,對於第二個參數只需要記住一點:傳遞數值,表示間隔時間

以上我們用的是setTimeout,setInterval和setTimeout二者的參數一模一樣,唯一的區別就是js語句執行的次數和時機。
setInterval是循環執行
setTimeout只執行一次

 1 var timer8_0 = setInterval(function(){
 2     console.log("我的微信號是:pelligit");
 3 },2000);
 4 
 5 var timer8_1 = setTimeout(function(){
 6     console.log("我的郵箱是:pelli_mail@163.com");
 7 },2000);
 8 
 9 // 我們來看看以上兩個定時器的執行情況
10 // timer8_0【從2s開始,每隔2s執行一次】
11 // 0s:
12 // 1s:
13 // 2s: "我的微信號是:pelligit"
14 // 3s:
15 // 4s: "我的微信號是:pelligit"
16 // 5s:
17 // 6s: "我的微信號是:pelligit"
18 // ......
19 // ---------------------------------
20 // timer8_1【只有第2s執行了一次】
21 // 0s:
22 // 1s:
23 // 2s: "我的郵箱是:pelli_mail@163.com"
24 // 3s:
25 // 4s:
26 // 5s:
27 // 6s:
28 // ......

實際開發中學到這里就基本上可以完成大多數定時器任務了。只需要掌握兩個參數:第一個是執行的語句,第二個是間隔時間。

那么我們現在來做一個小玩意兒吧。用兩種不同的方式做一個秒表。

秒表一共三部分。【開始】按鈕,【結束】按鈕,時間顯示區域。

點擊【開始】按鈕,時間顯示區域每一秒增加1。

點擊【結束】按鈕,時間顯示區域顯示0,停止數秒。

【HTML代碼】

<!-- 時間顯示區域 -->
<span id="seconds_content">0</span>

<!-- 開始按鈕 -->
<button type="button" id="start" name="button">start</button>
<!-- 結束按鈕 -->
<button type="button" id="stop" name="button">stop</button>

【JavaScript代碼】第一種,使用setInterval

(function(){
    // 顯示時間
    var seconds_content = document.getElementById("seconds_content");
    // 開始按鈕【點擊開始計時】
    var start_btn = document.getElementById("start");
    // 停止按鈕【點擊停止計時】
    var stop_btn = document.getElementById("stop");

    var count = 0;
    var time_count;

    // 給開始按鈕添加點擊事件,點擊按鈕,開始計時
    start_btn.addEventListener("click",function(event){
        seconds_content.innerHTML = count;
        time_count = setInterval(function(){
            count++;
            seconds_content.innerHTML = count;
        },1000);
    });

    // 給停止按鈕添加事件,點擊按鈕,停止計時
    stop_btn.addEventListener("click",function(event){
        clearInterval(time_count);
        count = 0;
        seconds_content.innerHTML = count;
    });
})();

【JavaScript代碼】第二種,使用setTimeout

(function(){
    // 顯示時間
    var seconds_content = document.getElementById("seconds_content");
    // 開始按鈕【點擊開始計時】
    var start_btn = document.getElementById("start");
    // 停止按鈕【點擊停止計時】
    var stop_btn = document.getElementById("stop");

    var count = 0;
    var time_count;

    function intervalLike(){
        time_count = setTimeout(function(){
            count++;
            seconds_content.innerHTML = count;
            // 循環調用自身,達到和setInterval一樣的效果
            intervalLike();
        },1000);
    }

    // 給開始按鈕添加點擊事件,點擊按鈕,開始計時
    start_btn.addEventListener("click",function(event){
        intervalLike();
    });

    // 給停止按鈕添加事件,點擊按鈕,停止計時
    stop_btn.addEventListener("click",function(event){
        clearInterval(time_count);
        count = 0;
        seconds_content.innerHTML = count;
    });
})();

最終效果如圖:

上面就是最簡單的秒表程序。實際開發中,還有很多地方需要優化。秒表寫完了,打開瀏覽器看看,是不是有點小興奮啊。點擊start,開始數秒,1,2,3,4,5,6,7,8......點擊停止,停止數秒。再點擊開始,1,2,3,4,5,6,7.....停止,開始,停止,開始,停止,開始,開始,開始,興奮過頭了,多點擊了幾次開始,,咦?怎么越來越快了?

看代碼可以發現有兩個方法和setTimeout和setInterval很像,分別是clearTimeout和clearInterval,由名字就可以看到,前面有一個clear,是清除的意思。這兩個都是用來清除定時器引用的

有setTimeout就有clearTimeout
有setInterval就有clearInterval

var timer9 = setTimeout(function(){
    console.log('hello world');
},2000);

//清除上面的定時器,hello world將不會輸出
clearTimeout(timer9);

timer9的值是setTimeout語句的一個標簽,是js引擎對這條語句的記錄值,是一個數字,可以傳遞給clearTimeout用來清除定時器。【以上代碼clearTimeout除了可以傳遞timer9之外,還可以傳遞和timer9相等的數字】
也就是說clearInterval和clearTimeout的參數是一個數值,該數值是指向要清除的定時器的一個引用地址值。
根據觀察發現,該值不是定時器代碼的執行所需要的時間,也不是代碼執行的間隔時間,單位不是毫秒數。而是隨着瀏覽器啟動的時間變長而增大。【谷歌瀏覽器中】
我猜測這是一個指向定時器的一個地址引用,因為clearInterval和clearTimeout可以直接傳遞和定時器語句返回值相同的數值。


具體情況,還得研究一下瀏覽器js引擎的底層。目前由於能力有限,暫未涉及。如果有朋友了解這個東西,歡迎補充完整。
IE瀏覽器和火狐瀏覽器都是遞增的,不過並不是從0開始的,也不是從1開始的。火狐是2,IE是5,谷歌瀏覽器的初始值就不確定了,隨着時間的推移,該值會越來越大,不過雖然與時間有關,但是其與時間的關聯方式目前還有待進一步研究,因為其既不是按着毫秒增長的,也不是按着秒增長的。

還記得之前的秒表越來越快的問題嗎?clearInterval和clearTimeout就是解決這個問題的。在適當的時間清除定時器是一個好的編碼習慣。

改進型秒表。再也不會越來越快了。不過計時不精確。

// 顯示時間
var seconds_content = document.getElementById("seconds_content");
// 開始按鈕【點擊開始計時】
var start_btn = document.getElementById("start");
// 停止按鈕【點擊停止計時】
var stop_btn = document.getElementById("stop");

var count = 0;
var time_count;

function setTimer(){
    time_count = setInterval(function(){
        count++;
        seconds_content.innerHTML = count;
    },1000);
}

// 給開始按鈕添加點擊事件,點擊按鈕,開始計時
start_btn.addEventListener("click",function(event){
    count = seconds_content.innerHTML;

    // 清除原先的定時器
    clearInterval(time_count);

    // 重新開始計時
    setTimer();
});

// 給停止按鈕添加事件,點擊按鈕,停止計時
stop_btn.addEventListener("click",function(event){
    clearInterval(time_count);
    count = 0;
    seconds_content.innerHTML = count;
});

讓我們來思考這樣一個問題。當定時器的第一個參數是一個匿名函數或者是函數名的時候,而這個匿名函數或者函數名剛好需要傳遞參數,這個時候我們該怎么辦?

就像下面這樣

function plus(a,b,c){
    console.log(a,b,c);
}

var timer10_0 = setTimeout(plus,1001);

var timer10_1 = setTimeout(function(a,b){
    console.log(a + b);
},1000);

這個時候,第三個參數,第四個參數,第五個參數......就發揮作用了。

function plus(a,b,c){
    console.log(a,b,c);
}

//hello world hi
var timer10_0 = setTimeout(plus,1001,"hello","world","hi");

var timer10_1 = setTimeout(function(a,b){
    console.log(a + b);//300
},1000,100,200);

就是這么簡單。

關於JavaScript定時器,可以說的東西太多了,這里暫時先介紹到這里。下面留幾個面試題,大家欣賞一下。

// 1.下面的代碼輸出名字的順序是什么?
console.log("小明");
var myname = setTimeout(function(){
    console.log("小華");
},0);
console.log("小麗");
// 2.下面的代碼,會彈出什么內容?
var alert_things = setTimeout(function(){
    alert("Pelli");
},2000);
while(true){}
// 3.下面的代碼,會輸出什么?
setTimeout(function(){
    console.log(this);
},1000);
// 4.下面的代碼,會輸出什么?
for (var i = 0; i < 3; i++) {
     setTimeout(function() {
         console.log(i);
     }, 0);
     console.log(i);
}
// 5.下面的代碼,會彈出哪些東西?
var len=4;
while(len--){
    setTimeout(function(){
        alert(len);
    },0);
    alert(len);
}
// 6.下面的代碼是什么結果?
var timer = setInterval(function(){
    console.log("hello world");
},0);

clearInterval(timer);
// 7.下面的代碼會彈出什么?
var t = true;
window.setTimeout(function (){
    t = false;
},0);
while (t){}
alert('end');

另外有一些開發過程中的最佳實踐:

  1.setTimeout和setInterval的第一個參數不建議傳遞字符串,傳遞字符串和eval有一樣的問題,會造成性能方面的問題,甚至引起安全問題。

  2.建議用setTimeout模擬setInterval,不建議使用setInterval。頁面中setTimeout和clearTimeout不配合使用基本上不會出現問題,因為setTimeout只執行一次,不會造成累加的問題,使用setInterval的話,強烈建議在適當時候使用clearInterval,在這種情況,很容易出現問題。

  

歡迎各位程序媛和我一起交流討論。男女通吃。

github地址:www.github.com/pelligit/

我是沛笠,Pelli,

微信號:pelligit

QQ:2653807423

 


免責聲明!

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



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