JS是單線程的,但瀏覽器是多進程多線程的
這里的JS線程指的是JavaScript引擎(如V8引擎),瀏覽器一般包含多個進程:瀏覽進程、渲染進程、插件進程,每個tab頁都有獨立的進程。
渲染進程一般包含多個線程:
JS引擎線程(主線程)、
GUI渲染線程(解析HTM、CSS,與JS引擎線程互斥)
HTTP網絡請求線程(一般由JS線程觸發)
定時器觸發線程(setTimeout、setInterval所在的線程,並不是js線程記時)
瀏覽器事件處理線程
其中js線程與GUI線程是互斥的。
JS引擎線程主要管理內存分配、執行調用棧
JavaScript 只是一個單線程的編程語言,這意味着它只有一個調用棧。這樣它只能一次做一件事情。
調用棧是一種數據結構,里面會記錄我們在程序中的大概位置。當執行進入一個函數,把它置於棧的頂部。如果從函數中返回則從棧頂部移除函數。這就是調用棧所能夠做的事情。
1 function multiply(x, y) { 2 return x * y; 3 } 4 5 function printSquare(x) { 6 var s = multiply(x, x); 7 console.log(s); 8 } 9 10 printSquare(5);
執行棧:
發生異常時,正好追蹤異常是如何構造出來的,基本就是打印出當前調用棧的狀態即可。
‘堆棧溢出’--當達到調用棧的上限時,就會發生堆棧溢出。遞歸代碼,條件設置不當時,容易發生堆棧溢出。
事件循環
JS是單線程的,且只有一個執行棧。為了更好的處理用戶交互,所有瀏覽器都內置了一種被成為事件循環(event loop)的處理機制。
事件循環只有一項簡單的工作-監測調用棧和回調隊列。如果調用棧是空的(JS引擎空閑時),它會從回調隊列中取得第一個事件然后入棧,並有效地執行該事件。
打個比方,當 JavaScript 程序發起 Ajax 請求來從服務器獲得數據,你在回調函數中書寫 "response" 代碼,JS 引擎會告訴宿主環境:
"嘿,我現在要掛起執行了,現在當你完成網絡請求的時候且返回了數據,請執行回調函數。"
HTTP網絡請求線程、定時器觸發線程、瀏覽器事件處理線程都會在事件返回或到達時間后,向回調隊列添加回調事件。