JavaScript中的setTimeout和setInterval

上一篇博文<瀏覽器中Javascript單線程分析>中描述了瀏覽器中Javascript單線程的原理。html

在此基礎上,這篇文章將主要介紹setTimeout/setInterval是如何模擬異步的,且兩者之間又有何區別。瀏覽器

首先咱們來分析它們如何模擬異步。異步

能夠根據上篇博文了解到JS引擎內部維護一個隊列,用來存放各類回調函數,其中也包括setTimeout/setInterval回調。函數

下面用代碼結合圖形的方式來描述異步是如何產生的。spa

先看例1:線程

<html>
<body>
<script>
    setTimeout(function(){
        console.log(1);
    },0);
    console.log(2);
</script>
</body>
</html>

輸出結果爲:2 13d

其隊列可描述爲:code

其中code snippet指<script>裏的邏輯代碼段;setTimeout callback指定時器回調。htm

雖然setTimeout設爲0ms,但當前有代碼段在執行,只能將回調置於隊列後面。blog

再看例2:

<html>
<body>
<script>
    setInterval(function(){ console.log(1); },0); console.log(2); </script> </body> </html>

輸出結果爲:2 1 1 1 1 ...

其隊列可描述爲:

至於爲何不是 1 1 1 ... 2,原理和setTimeout(..,0)是一致的。

從上看到經過回調隊列實現了延時函數setTimeout/setInterval。

那麼問題來了,這兩個函數的延時是否精確,也就是說是否按照設置的延時時間執行?

接着看例3 setTimout:

<html>
<body>
<script>
    setTimeout(function(){
        console.log(1);
    },1000);
    //耗時處理超過1s,假設約2s
doSomething(); </script> </body> </html>

結果:延遲輸出1會在doSomething()執行後大約2s後執行。

這是爲何,咱們能夠用上一篇博客的原理來解釋,仍是用隊列圖來描述:

再來看例4 setInterval:

<html>
<body>
<script>
    setInterval(function(){
        console.log(1);
    },10);
    //耗時處理超過1s,假設約2s
doSomething(); </script> </body> </html>

結果:延遲輸出第一個1會在doSomething()執行後大約2s後執行,緊接着立刻輸出第二個1,時間間隔並無10ms。

之因此間隔沒有10ms,是由於定時回調在插入到隊列時發現預期時間點被doSomething佔用,只能後延插入的回調,

這樣會致使第二次定時回調會直接放在第一個定時回調後,因此在執行時沒有間隔。

隊列圖以下:

從上面的結果能夠回答咱們以前提出的疑問,setTimout/setInterval不能保證必定在延時達到時執行回調。

既然不能保證延時時間,那產生上面的結果的緣由又是什麼呢?且兩個函數在延時上有什麼區別呢?

讓咱們來看例5:

<html>
<body>
<script>
setTimeout(function(){
//耗時函數,假設爲2s
doSomething();
setTimout(arguments.callee,1000);
},1000); setInterval(function(){
//耗時函數,假設爲2s
doSomething(); },1000); </script> </body> </html>

從邏輯意義上,若是setTimeout/setInterval延時沒有偏移,那麼兩段斷碼意義一致。

但從上面實驗結果代表兩者均存在延時偏移,那麼偏移是怎麼產生的,且兩者偏移有何不一樣?

仍是用兩個隊列來描述着兩段代碼的執行結果。

setTimeout:

setInterval:

從上咱們即可以清楚地看到,setTimeout每次遞歸會等待上一次回調函數執行完成,而回調中存在耗時函數,因此會超過1s才能執行下一次回調;

而setInterval不會等待回調執行完成,而是將回調每隔1s插入到回調隊列中,若是該時間點存在其餘回調,則該時間點的回調日後移動,後面的定時回調時間點不受影響,

由於回調中存在耗時函數,因此定義器不能恰好將回調插入預設的時間點上,而只能插入到耗時函數後,這會致使後面的定時回調函數堆積,執行時時間間隔不會超過1s。

圖中層疊關係表明定時回調函數堆積(有序)。

總的來講,也就是setTimeout延時間隔可能大於設置的時間,而setInterval延時間隔可能小於設置的時間。

關於瀏覽器單線程原理和setTimout/setInterval原理就介紹到這。

相關文章
相關標籤/搜索