上一篇博文<瀏覽器中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原理就介紹到這。