- 瀏覽器 GUI 渲染線程
- JavaScript 引擎線程
- 瀏覽器定時觸發器線程
- 瀏覽器事件觸發線程
- 瀏覽器 http 異步請求線程
瀏覽器 GUI 渲染線程 和 JavaScript 引擎線程之間是互斥的
在debug裏面check一下以下代碼的效果便可知道html
var sleep = function(time) { var date = new Date(); while(new Date() - date <= time) {} } document.body.innerHTML = '123'; sleep(3000)
原理也很簡單:瀏覽器
- GUI渲染線程負責渲染瀏覽器界面的HTML元素, 當界面須要重繪(repaint)因爲某種操做引起迴流(reflow)時,該線程就會執行。
- 由於 JavaScript腳本是可操縱DOM元素, 在修改這些元素屬性同時渲染界面, 那麼渲染線程先後得到的元素數據就可能不一致了。
- 因此在腳本中執行對界面進行更新操做, 如添加結點,刪除結點或改變結點的外觀等更新並不會當即體現出來, 這些操做將保存在一個隊列中,待JavaScript引擎空閒時纔有機會渲染出來。
因此當遇到在js引擎運算量較大的時候, 又想及時調用GUI渲染,能夠作以下修改:併發
var sleep = function(time) { var date = new Date(); while(new Date() - date <= time) {} } document.body.innerHTML = '123'; setTimeout(function() { sleep(3000); }, 0);
JavaScript 引擎線程
事件驅動(event driven)機制
事件驅動通常經過事件循環(event loop)和事件隊列(event queue)來實現的。app
假定瀏覽器中有一個專門用於事件調度的實例(該實例能夠是一個線程,能夠稱之爲事件分發線程event dispatch thread),該實例的工做就是一個不結束的循環,從事件隊列中取出事件,處理全部很事件關聯的回調函數(event handler)。異步
注意回調函數是在Javascript的主線程中運行的,而非事件分發線程中,以保證事件處理不會發生阻塞。 函數
Event loop codeoop
若是隊列非空,引擎就從隊列頭取出一個任務,直到該任務處理完,即返回後引擎接着運行下一個任務, 在任務沒返回前隊列中的其它任務是無法被執行的.spa
與其它線程間的通訊
瀏覽器 http 異步請求線程
瀏覽器對同一域名進行請求的併發鏈接數是有限制的,result: 10 in IE 8, 6 in Firefox 3+ and Chrome 5+。詳情參考demo: http://developer.oncecode.com/comet/線程
XMLHttpRequest在鏈接後是經過瀏覽器新開一個線程請求, 將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件放到 JavaScript引擎的處理隊列中等待處理debug
瀏覽器事件觸發線程
接受瀏覽器裏面的操做事件響應。如在監聽到鼠標、鍵盤等事件的時候, 若是有事件句柄函數,就講對應的任務壓入隊列。
瀏覽器定時觸發線程
瀏覽器模型定時計數器並非由JavaScript引擎計數的, 由於JavaScript引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確, 它必須依賴外部來計時並觸發定時。
setTimeout & setInterval
var sleep = function(time) { var date = new Date(); while(new Date() - date <= time) {} } var time = new Date(); var a = setInterval(function(){ sleep(200); console.log(new Date() - time); if(new Date() - time > 700) clearInterval(a); }, 100); //output 303 506 708
以上代碼表示在700時間內,一共有7個timer callback被壓入隊尾, clearInterval後, 只有3個timer對應的callback獲得執行。 因此這種狀況能夠採用setTimeout的方式來讓執行結果更加符合開發預期:
var a = setTimeout(function(){ sleep(200); setTimeout(arguments.callee, 100) }, 100);