一、發生一個事件時,事件及事件處理程序會被放入瀏覽器的事件隊列,事件可歸為以下幾類:
瀏覽器事件:window.load、document.DomContentLoaded等
網絡請求事件:ajax、websocket
用戶事件:單雙擊、鼠標滾動、調整頁面大小等
計時器事件:setTimeout,setInterval
事件處理程序如果是function(){}格式,則里面的this指向綁定事件的dom節點對象,跟e.currentTarget一樣(即便是事件委托也一樣),如果采用()=>{}格式,this指向的是window
二、事件的執行是異步的。
三、對事件隊列的處理(放入及執行)是瀏覽器的渲染進程(又叫內核進程、render進程)負責,渲染進程分為以下線程:
1.GUI線程
負責把html解析成dom tree,把css解析成css rule,然后把兩者結合,形成render tree。然后計算出layout tree放入瀏覽器內存中供Browser進程顯示到界面上;
當界面需要重繪或回流時,該線程會被執行;
該線程與JS引擎線程是互斥的,當JS引擎在執行時,該線程會處於暫停狀態。這也就是為什么一段JS代碼執行時間比較久時頁面會出現空白或卡頓的原因
2.JS引擎線程
又叫JS內核,負責解析和執行JS代碼;
當JS引擎執行到事件代碼語句時(比如addEventListener),會通知事件線程需要關注XXX事件;
3.事件觸發線程
主要負責事件隊列的維護
當事件觸發時會被事件線程監聽到,事件線程把事件處理程序放入到事件隊列中,這一過程不會打斷JS引擎線程的執行
4.定時器觸發線程
setTimeout與setInterval所在的線程;
由於js引擎是單線程的,如果處於阻塞線程狀態,則會影響記時的准確性,因此需要單獨的線程來計時並觸發事件
當到達指定時間時,setTimeout回調代碼也會被加入到事件隊列,注意是加入事件隊列,而不是立即執行,因為如果在隊列中還有其他待執行的代碼時就不會執行;
setInterval的情況更特殊,當到達指定時間時,如果上一次的回調函數還在隊列中等待執行,則直接跳過而不做任何操作。
5.異步請求線程
XmlHttpRequest所在線程;
假如不采用單獨的線程而是直接使用JS引擎線程,從發出請求到等待服務端給出響應的這段時間,JS引擎就會一直處於運行狀態,原本可以繼續執行后面的代碼的,現在只能等待
四、JS是單線程運行的的、那Worker為何可以達到多線程的效果?
為什么JS是單線程執行的?假如JS是多線程執行的,一個線程要添加DOM、另一個線程要刪除DOM,就會亂套,這就像兩台電腦不能同時使用同一台打印機一樣的道理;
創建WebWorker時,JS引擎線程向瀏覽器申請了worker專用的線程,worker線程(子線程)與JS引擎線程(主線程)之間通過postMessage Api進行線程間的通訊,可以理解為瀏覽器給JS引擎線程開了外掛;
ShardWorker是瀏覽器中所有tab標簽共享的,每個tab標簽都有獨立的render進程,所以SharedWorker不屬於某個Render進程,而是瀏覽器開了獨立的進程來管理。
五、事件循環機制
當事件觸發線程監測到事件執行時會把事件處理程序放入隊列以便事件循環使用,事件隊列分為宏任務隊列和微任務隊列
宏任務:setTimeout,setInterval,setImmediate,requestAnimationFrame,I / O,UI呈現,js同步代碼等
微任務:Promises,Object.observe,MutationObserver
事件循環機制的執行過程如下:
