文章目錄
javascript是單線程的
javascript可以操作DOM,所以javascript是單線程的
。
爲啥?
試想下,如果javascript是多線程的,就比如現在有兩個線程,其中一個是給DOM節點添加內容,另一個是刪除這個DOM節點,這時瀏覽器應該聽誰的。爲了避免這類複雜的同步問題,javascript就被設計成單線程
的了。
那Web Worker
怎麼說?
雖然允許使用Web Worker
創建子線程,但子線程完全受控於主線程,且子線程不能操作DOM,所以Web Worker
並沒有改變javascript是單線程的
這一本質。
瀏覽器內核是多線程的
javascript是單線程的
就意味着, 只有等待上一個任務執行完,下一個任務纔會開始執行。
就像醫院排隊候診一樣,如果因爲病情複雜需要醫生花費更多的時間去分析診斷比如,任務計算量大,不得不讓大家多等會兒,可以理解。但是,輪到一位女士時,她卻說“哎呀呀,我得回家取趟病例先,再等我two hours”。讓大家空等?當然不會,醫生會讓這位“麻煩女士”先回家取病例,等她取回病例了,她必須在“等候區”等待,直到醫生將之前排隊的病人全部診斷完畢,空閒下來了,才輪到這位“麻煩女士”。
這個"等候區"就是任務隊列
。任務隊列
裏有什麼?那我們就得了解下瀏覽器內核裏有啥線程。
瀏覽器內核裏的線程
定時器線程
setTimeout
和setInterval
不是由js引擎計時,由瀏覽器內核的Timer模塊處理。
setTimeout
和setInterval
所在的線程就是定時器線程。
當計時完成,回調函數就會進入任務隊列
。
事件觸發線程
onclick
、onmouseover
等這些事件也不是js引擎的,由瀏覽器內核的DOM binding模塊處理。
當事件觸發時,事件處理程序會進入任務隊列
。
異步http請求線程
Ajax也不是js引擎的,由瀏覽器內核的Network模塊處理。網絡請求返回後,對應的回調函數也會進入任務隊列
。
javascript引擎線程
javascript引擎線程
就是javascript線程
、主線程
,用來執行javascript代碼。
主線程
空閒後,纔會執行任務隊列
中的任務。
GUI線程
負責渲染頁面,包括解析HTML、CSS,構建DOM樹、Render樹,佈局、繪製。
頁面重繪或迴流,就會運行這個線程。
你肯定聽說過,"script
標籤的加載和解析會阻塞DOM渲染"或者遇到過類似下面的問題。
“script
標籤的加載和解析”,就是執行js代碼,即運行javascript引擎線程
;
“DOM渲染”,就是運行GUI線程
。
是的,GUI線程
與javascript引擎線程
是彼此互斥
的,有你沒我,有我沒你。
javascript引擎線程
運行時,GUI線程
是凍結的,只有javascript引擎線程
空閒時,GUI線程
纔會運行。
事件循環
- 同步任務
所有的同步任務
都在主線程
javascript引擎線程上執行,只有上一個任務執行完畢,下一個任務纔會開始執行。
- 異步任務
異步任務
經對應的異步線程
處理後進入任務隊列
。比如,setTimeout
經定時器線程
處理後,其回調函數將被放入任務隊列
。
所有同步任務
都在主線程
上執行,因此形成一個執行棧
。
主線程
在運行的過程中,遇異步任務
會將其交給對應的異步線程
。經異步線程
處理後異步任務
會被放入任務隊列
;主線程
執行完所有同步任務
,空閒時,會從任務隊列
中讀取異步任務
,此時,異步任務
將結束等待狀態進入執行棧
,開始在主線程
上執行。
1、2重複,就是事件循環
。
舉個簡單的例子吧。
setTimeout(bless,0);
function sayHi(){
console.log("hello world");
}
function bless(){
console.log("have a nice day");
}
sayHi();
console.log("nice to meet you");
console.log("nice to meet you too");
console.log("how are you");
console.log("I'm fine");
console.log("thank you");
console.log("and you");
這裏,bless
就是一個異步任務
,sayHi
和後面一羣console.log
就是同步任務
,所以"have a nice day"
會在最後輸出。