認識process與thread之前,首先要先認識同步與非同步的演變
同步/非同步
同步
一次做一件事情,JavaScript是單一執行緒,因此只會在同一時間執行一個task。
現在有以下的程式,會逐漸的放入callstack,再pop出來執行:
1 | const second = () => { |
不過同步有個問題是,如果中間有一大坨東西卡住,是不是就動不了了?
1 | console.log('1'); |
看起來很困繞對吧?必須要等到中間的任務完成(或是根本無法完成)才能繼續執行下面的程式。
非同步
解決同步問題的方式就是使用asynchronous callbacks,透過event loop解決單一執行序的困擾,達成非同步的效果。
事實上在開發的過程就有用到很多非同步的功能了,例如:API request, web API, document…
V8 engine
在過去JavaScript都是直譯執行的,但是在V8 engine出現後,結合混合編譯與直譯的方式執行程式(Just In Time),讓JavaScript提升大量的效能。至於V8的執行方式又是另一個世界了,不過大概可以整理出幾個重點:
- engine主要包含memory heap,包含宣告的variable與function。callstack,用來放置可執行的單位。
- setTimeout之類的web API並不包含在engine內
- 透過web API可以callback function
- 一旦callstack空了,會夠過Event loop推送Event Queue到callstack執行
callback原理
假設你希望程式先執行getUser再執行storeUser。
1 | function example() { |
但是需要等待getUser拿到資料才可以儲存資料呀,所以可以寫成callback:
1 | function example() { |
如果你了解event loop原理的話,example會先丟入callstack產生callback,執行完再丟入getUser。
但是這樣的程式碼很容易讓人混淆,不好維護。
Job Queue/Micro task - ES6
ES6推出Promise之後,解決了非同步需要撰寫大量的callback的狀況,Job Queue也隨之出現了,跟以前的Message Queue不同的地方在於,Job Queue的優先權較高,但是為了統一稱呼,我們都會叫做Event Queue。
以下是Promise非同步範例:
1 | function example() { |
儘管不用寫很深的callback hell,但是換來的是有不斷地then出現…
async/await - ES7
在ES7的Promise出了非同步的Sugar,它的出現讓程式碼更簡潔,真的是拯救蒼生了呀。
1 | async function example() { |
Nodejs特色
- 不會阻塞 non-blocking
- 事件驅動 Event-driven
- 資料密集 - data intensive
- I/O密集 - I/O intensive
Nodejs是單執行緒在執行,它並不會等待其他事件做完再繼續執行,他會不斷的執行接下來的任務,不會blocking。
儘管程式執行到event的程式碼,但是事件觸發才會真正的執行,這就像JS的Bubbling和Capturing一樣,事件發生而驅動事件。
假設有十個使用者連線Server,其中一位使用者想要抓取一個很大件的資料,如果卡在這位使用者身上,那麼其他9位就別想使用服務了對吧?但是Nodejs並不會影響到其他9位使用者,一樣會正常的給予response。
Nodejs不擅長
- 資料計算
- Processor intensive
- blocking operation