Javascript引擎单线程机制及setTimeout执行原理说明
setTimeout用法在實際項目中還是會時常遇到。比如瀏覽器會聰明的等到一個函數堆棧結束后才改變DOM,如果再這個函數堆棧中把頁面背景先從白色設為紅色,再設回白色,那么瀏覽器會認為DOM沒有發生任何改變而忽略這兩句話,因此我們可以通過setTimeout把“設回白色”函數加入下一個堆棧,那么就可以確保背景顏色發生過改變了(雖然速度很快可能無法被察覺)。
總之,setTimeout增加了Javascript函數調用的靈活性,為函數執行順序的調度提供極大便利。
然后,我們從基礎的層面來看看:理解JavaScript的定時器是如何工作的是非常重要的。
計時器的執行常常和我們的直觀想象不同,那是因為JavaScript引擎是單線程的。我們先來認識一下下面三個函數是如何控制計時器的。
var id = setTimeout(fn, delay);?- 初始化一個計時器,然后在指定的時間間隔后執行。該函數返回一個唯一的標志ID(Number類型),我們可以使用它來取消計時器。
var id = setInterval(fn, delay);?- 和setTimeout有些類似,但它是連續調用一個函數(時間間隔是delay參數)直到它被取消。
clearInterval(id);,?clearTimeout(id);?- 使用計時器ID(setTimeout 和 setInterval的返回值)來取消計時器回調的發生
?
有一個基本的概念你得記住了:
時間延遲不能被保證。什么意思,就是說你這樣寫setTimeout(fn, 500)并不代表fn肯定在500毫秒之后馬上就執行,延遲很可能會更長。因為 JavaScript 是單線程語言,所有的異步事件(包括計時器、鼠標事件或者一個 XMLHttpRequest 完成)僅僅當程序執行期間有缺口的時候才會執行,不是你規定了什么時候就什么時候執行,要知道程序員不是萬能的,你寫的東西最終還是要看瀏覽器臉色的。
?
用一個很好的圖表加以說明:
?
?
JavaScript引擎用單線程運行也是有意義的,單線程不必理會線程同步這些復雜的問題,問題得到簡化.
那么單線程的JavaScript引擎是怎么配合瀏覽器內核處理這些定時器和響應瀏覽器事件的呢?
下面結合瀏覽器內核處理方式簡單說明.
瀏覽器內核實現允許多個線程異步執行,這些線程在內核制控下相互配合以保持同步.假如某一瀏覽器內核的實現至少有三個常駐線 程:javascript引擎線程,界面渲染線程,瀏覽器事件觸發線程,除些以外,也有一些執行完就終止的線程,如Http請求線程,這些異步線程都會產 生不同的異步事件,下面通過一個圖來闡明單線程的JavaScript引擎與另外那些線程是怎樣互動通信的.雖然每個瀏覽器內核實現細節不同,但這其中的 調用原理都是大同小異.
由圖可看出,瀏覽器中的JavaScript引擎是基于事件驅動的,這里的事件可看作是瀏覽器派給它的各種任務,這些任務可以源自 JavaScript引擎當前執行的代碼塊,如調用setTimeout添加一個任務,也可來自瀏覽器內核的其它線程,如界面元素鼠標點擊事件,定時觸發 器時間到達通知,異步請求狀態變更通知等.從代碼角度看來任務實體就是各種回調函數,JavaScript引擎一直等待著任務隊列中任務的到來.由于單線 程關系,這些任務得進行排隊,一個接著一個被引擎處理.
?
?
IE8及其之前的IE版本更新間隔為15.6毫秒。假設你設定的setTimeout延遲為16.7ms,那么它要更新兩個15.6毫秒才會該觸發延時。這也意味著無故延遲了 15.6 x 2 - 16.7 = 14.5毫秒。
? ? ? ? ? ?16.7ms
DELAY: |------------|
CLOCK: |----------|----------|
? ? ? ? ?15.6ms ? ?15.6ms
所以即使你給setTimeout設定的延時為0ms,它也不會立即觸發。目前Chrome與IE9+瀏覽器的更新頻率都為4ms(如果你使用的是筆記本電腦,并且在使用電池而非電源的模式下,為了節省資源,瀏覽器會將更新頻率切換至于系統時間相同,也就意味著更新頻率更低)。
退一步說,假使timer resolution能夠達到16.7ms,它還要面臨一個異步隊列的問題。因為異步的關系setTimeout中的回調函數并非立即執行,而是需要加入等待隊列中。但問題是,如果在等待延遲觸發的過程中,有新的同步腳本需要執行,那么同步腳本不會排在timer的回調之后,而是立即執行
?
讓我們用一個例子來闡明setTimeout和setInterval之間的區別:
? setTimeout(function(){
? ??/* Some long block of code... */
? ? setTimeout(arguments.callee,?10);
??},?10);
??
? setInterval(function(){
? ??/* Some long block of code... */
??},?10);
?
這兩句代碼乍一看沒什么差別,但是它們是不同的。setTimeout回調函數的執行和上一次執行之間的間隔至少有10ms(可能會更多,但不會少于10ms),而setInterval的回調函數將嘗試每隔10ms執行一次,不論上次是否執行完畢。
在這里我們學到了很多知識,總結一下:
JavaScript引擎是單線程的,強制所有的異步事件排隊等待執行
setTimeout?和?setInterval?在執行異步代碼的時候有著根本的不同
如果一個計時器被阻塞而不能立即執行,它將延遲執行直到下一次可能執行的時間點才被執行(比期望的時間間隔要長些)
如果setInterval回調函數的執行時間將足夠長(比指定的時間間隔長),它們將連續執行并且彼此之間沒有時間間隔。
?
參考:
一家之言:說說 JavaScript 計時器的工作原理_大前端
?
http://www.daqianduan.com/1112.html??
JavaScript的單線程性質以及定時器的工作原理 - Rain man - PHP博客
?
http://www.phpweblog.net/rainman/archive/2009/01/05/6267.html?
JavaScript可否多線程? 深入理解JavaScript定時機制 - 前端交互 - PHP5研究室
?
http://www.phpv.net/html/1700.html? ?轉載于:https://www.cnblogs.com/answercard/p/3852795.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Javascript引擎单线程机制及setTimeout执行原理说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动广告平台有哪些,各有啥优缺点?
- 下一篇: Nginx内核参数相关的优化设定