今天面試的時候被問到一個問題,是關於 JS 非同步的。當時我腦海中閃過了一個單線程的概念,但卻沒有把真正的原理闡述清楚。所以回來特意重新回顧了前面單線程和非同步相關的一些知識點。 雖然之前學習的時候也接觸了單線程模型相關的東西,但當時理解得並不是很清楚和明白。所以這道面試題也沒有給出一語中的的答案。重新 ...
今天面試的時候被問到一個問題,是關於 JS 非同步的。當時我腦海中閃過了一個單線程的概念,但卻沒有把真正的原理闡述清楚。所以回來特意重新回顧了前面單線程和非同步相關的一些知識點。
雖然之前學習的時候也接觸了單線程模型相關的東西,但當時理解得並不是很清楚和明白。所以這道面試題也沒有給出一語中的的答案。重新閱讀阮一峰的 《JavaScript 運行機制詳解》和我之前寫的《setTimeout 非同步與回調》之後。我決定重新寫一篇博客來更加白話的總結 JS 的單線程機制和非同步。
重演歷史 - 為什麼 JS 是單線程
當我們打開一個頁面,頁面的 JS 會去執行。從系統層面來說,他是單線程的去執行。換句話講系統中只有一個線程去處理整個頁面的渲染(代碼的執行)。為什麼會這樣呢?因為簡單,因為 JS 很早就被髮明出來了,瀏覽器也是。那個時候考慮的第一件事,是功能簡單。試想一下如果是多線程,一個處理頁面渲染 DOM 操作,另外的線程也處理,就會出現混亂。因為搞不清楚誰做了什麼,做到什麼程度了。但如果只有一個線程單獨處理這些操作,就非常好的能夠管控。所以瀏覽器的模型就是單線程。
HTML5
提出Web Worker
標準,允許JavaScript
腳本創建多個線程,但是子線程完全受主線程式控制制,且不得操作DOM
。所以,這個新標準並沒有改變JavaScript
單線程的本質。
所以在早期,當使用大量同步的代碼時,打開網頁就會出現卡屏、卡頁面的情況。因為在獲取數據的時候,使用同步的代碼,後面的數據還沒到來之前,獲取數據之後的代碼都無法執行。只有等獲取數據全部結束後,才會執行下麵的代碼。
所以後來,大家都知道了。開始使用非同步。
同步隊列 / 任務隊列
既然是單線程,就意味著需要排隊,即前面的任務結束,後面的任務才能執行。所以所有的任務被分成了兩種,一種是同步任務,一種就是非同步任務。同步任務指的就是在主線程上排隊執行的任務,只有前一個任務執行完畢,才能繼續執行後一個任務。而非同步任務指的是,不進入主線程(同步隊列 stack
),而是進入任務隊列(task queue
)的任務。只有任務隊列通知主線程,某個非同步任務可以執行了,該任務才會進入主線程執行。
只要主線程空了,就會去讀取 "任務隊列" ,這就是
JavaScript
的運行機制。這個過程會不斷重覆。
重新理解這張圖
例如我們有一段代碼,這些代碼是用來做一些操作。正常情況下,代碼都是放置在棧(stack
)中的,正常執行的時候是一個個去執行的。但是有些時候,比如給頁面元素綁定一個事件,發了一條 ajax
請求,或者還有一些非同步處理。這個時候,就不能夠放置在 stack
中了。不然就會處於長久的等待之中,其他的代碼就無法按順序即使的執行。
所以在瀏覽器裡面,還有一個可以被稱之為任務隊列的地方。對於一些非同步的任務,都是放到任務隊列裡面。舉例來說,當用戶給一個元素綁定事件 onClick
,綁定事件的操作在 stack
執行,但 onClick
回調本身是放到任務隊列裡面的。
重新理解 setTimeout
之前認識 setTimeout
時,就已經見過這段代碼了,他的輸出結果是先輸出 2
,後才輸出 1
。原理也是因為單線程機制。
setTimeout(function(){ console.log(1) },0) console.log(2)
即 console.log(2)
被放入到 stack
中執行,setTimeout()
被放入到 callback queue
了。換句話說,必須要等所有 stack
中的同步的代碼全都執行完後,才會去執行非同步的事件。所以不管 setTimeout()
裡面的時間是 0
還是 1000
都會被排到後面。
所以下麵這個例子,1
永遠不會被輸出,且 2
被迴圈多次後,會被卡死。因為永遠都在執行 console.log(2)
這個同步代碼。而整個 stack
棧都被堵死了。 setTimeout()
根本沒辦法執行。
var isOk = true setTimeout(function(){ console.log(1) isOk = false },1000) while(isOk){ console.log(2) }
分析原生 Ajax
所以再去分析原生 Ajax
裡面的代碼,也能夠更好的理解同步任務和非同步任務。
這次,我想我真的理解了 JavaScript
的單線程機制。
博客園大佬比較多... 由於本人水平有限。如果這篇博文中一些地方有錯誤,或者各位大佬覺得我還是沒有理解單線程機制和非同步,請輕噴...