Javascript中的setTimeout黑魔法

編程語言 JavaScript 腳本語言 技術 coder分享 2017-06-12

前言

如果寫過js代碼的人對於setTimeout方法一定不會感到陌生。setTimeout是一種定時器,在前端開發中有很多的應用場景,比如在購物車結算成功後,等待幾秒會自動跳轉至列表頁。今天我們就深入的看下setTimeout的實現原理。

Javascript中的setTimeout黑魔法

Javascript之setTimeout

基本用法

根據W3C的標準解釋,setTimeout是定義一個在指定時間後觸發的函數。

我們先來看看setTimeout的基本用法,實現這樣一個簡單的效果,點擊一個button,在3秒後頁面上的文字消失。

Javascript中的setTimeout黑魔法

setTimeout基本用法

由於這段代碼非常基礎,這裡不做過多講述。

setTimeout(fn, 0)

上面一部分說到setTimeout是相當於給函數定義一個‘鬧鐘’,當到了指定的時間後就會自動執行函數。但是如果我們將時間設置為0,即出現setTimeout(fn, 0)這樣的代碼,情況是怎麼樣的呢?是會立即執行嗎?

我們可以通過以下一段代碼來進行測試。

Javascript中的setTimeout黑魔法

測試代碼

如果和我們猜測的一樣,立即執行的話,上面的測試代碼會按照1 > 2 > 3的順序輸出,但是實際運行後,我們發現輸出結果的順序為1 > 3 > 2,而且不管運行多少次結果都不變。

出現了這樣的結果,就證明了setTimeout(0)並不是立即執行的,那這又該怎麼解釋呢?

JS單線程執行

為了解釋上面這個問題,我們要追溯到JS執行過程,我們都知道JS是單線程執行,所有的異步事件,包括自定義的頁面DOM事件,定時器,Ajax請求都會被添加到一個任務列表按照順序執行。

因為JS腳本文件是運行在瀏覽器端的,我們的JS引擎雖然是單線程的,但是對於瀏覽器來說確是多線程的。瀏覽器中不僅包括JS引擎,還包括網絡請求Ajax,瀏覽器渲染等,它們都有特定的線程去執行。

setTimeout並不能作為多線程使用,可以通過以下一段代碼來證明。

Javascript中的setTimeout黑魔法

測試代碼

對於以上一段代碼,如果setTimeout可以作為多線程使用,則新的線程會在一秒後將isEnd屬性設置false,那麼在一秒後會alert出end字符串。

但是實際情況確是,頁面從未打印出end字符串,而且頁面會呈現鎖死狀態,這是因為isEnd變量值並未修改為false,相當於執行while(true),最終頁面會崩潰。這也就能證明JS引擎是單線程執行狀態。

事件隊列

既然JS引擎是單線程執行,那麼setTimeout定義的事件該具體何時觸發呢?

這裡我們需要深入到瀏覽器內核設計,在內核中涉及到一個事件隊列的概念,我們可以直接看如下這張圖。

Javascript中的setTimeout黑魔法

事件隊列

從上面這張圖很容易看出,在瀏覽器內核中包含了各式各樣的線程,有瀏覽器GUI渲染線程,Javascript引擎線程,網絡請求線程。

在當JS引擎執行到其他線程相關的代碼時,就會執行其他線程的代碼,在其他線程執行完畢後需要JS引擎重新運行時,就會在JS引擎的事件隊列裡添加一個任務。

現在我們來看看setTimeout(0)做了什麼?它會開啟一個定時器線程,並不會影響後續的代碼執行,這個定時器線程會在事件隊列後面添加一個任務,例如上面圖中的t3。等到前面的t1,t2執行完後再去執行t3,因此在前面第二部分內容中的輸出順序為1 > 3 > 2。

setInterval

既然說到了setTimeout,就不得不提到setInterval,setInterval同樣作為一種定時器,是在指定的時間間隔後執行相應的函數。

一種最常見的場景是頁面上的倒計時實現。這裡我們實現一個簡單的效果,指定一個時間,並進行倒計時。

Javascript中的setTimeout黑魔法

倒計時

對比

setTimeout與setInterval雖然都是定時器,但是在執行上還是有不一樣的。

  1. setTimeout是指定的時間後執行一次;setInterval是在每隔指定的時間後執行多次。

  2. setTimeout(fn1, t1),fn1的執行時間是大於或等於t1的;setInterval(fn2, t2),fn2的執行會始終嘗試在t2時間後執行,如果網絡請求較大的話,會出現fn2連續執行的情況。

總結

今天這篇文章主要講解了Javascript中的setTimeout用法和執行原理,以及與setInterval的簡單比較,大家學會了嗎?

相關推薦

推薦中...