今天是這個系列的開始,我正職開始寫 node 約有半年,在這個領域仍然正在學習。
JS 的生態系非常的活躍,而且充滿了很多好玩的東西,從來不會覺得無聊。
雖然戰語言是一件無聊的事,這種興趣雖然幼稚,但卻很有趣啊~
我最喜歡看的漫畫類型就是類似終末的女武神那種 呂布對戰雷神索爾、或是某某跟某某打起來,到底誰會贏呢??
程式語言上也是如此,我喜歡寫 node,所以也要知道它的優點是什麼,缺點是什麼!又有什麼有趣的玩具存在於 node 的生態系,這樣我才知道 node 在對抗其他語言,到底誰會贏(?
ps. 本系列文並不是從頭教學文,所以想要從頭學 node 的人請轉彎,但對 node、npm、yarn,稍微有點了解,知道基礎JS 的,應該可以讀,並且歡迎和我一起分享心得。
正文開始。
Node.js 是 Javascript 的一個 runtime,而 runtime 便是可以執行 程式語言的一個環境,因此可以理解為只要有了 runtime,Js 便可以在不同的作業系統及平台去執行,而 Node 便是這樣的東西。
As an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications.
官網提到,Node 是一個非同步且事件驅動的 runtime,這是什麼意思呢?
一般的 web server 多半都是基於多執行序(multi-threaded)的方式執行的,這種方式會在接收每個 web request 時產生對應的 thread,並且相對消耗記憶體資源。在高併發的場景下,CPU 及 memory 並不足以應付,並且會產生較多的上下文切換(Context-switching)的成本。
而 Node 本身便是為了應付這種場景而被創造出來的工具,如上面提到的 Node 是一個非同步且事件驅動 runtime,而幫助他做到這件事的便是(single-threaded)。
什麼是同步呢?
在同步執行的情況下,我們的程式碼中的任何操作,都會有條有序地一一被執行。
const fs = require('fs');
const data = fs.readFileSync('./input.txt', {encoding:'utf8', flag:'r'});
console.log(data, 'Read completed!');
以上述例子來講,我們使用了 fs.readFileSync 這個方法來讀取 input.txt 這個檔案,因為讀取檔案本身是需要時間的(從硬碟讀取檔案對於程式執行來講是個相對十分慢的動作),在這個範例中程式執行到第二行,會因為讀取檔案需要等待,而在這邊造成阻塞(blocking),直到檔案內容讀取完畢後,才會在下一行印出檔案內容 及 'Read completed!'
然而為了讓單執行序的機制順利發揮,Node 的 API 支援許多非同步的語法,讓開發者在尤其耗時的 IO 操作上,可以不造成執行緒阻塞。
以下是 readFile 的非同步版語法,雖然檔案還未讀取完,但 Read not completed! 字樣的印出會先被執行,而 Read completed! 字樣則會在讀取完成後被印出。
var fs = require('fs');
fs.readFile('./input.txt', function (err, data) {
if (err) throw err;
console.log(data, 'Read completed!');
});
console.log('Read not completed!');
由於 web service 是典型的 IO bounded 的應用類型,所以正好符合 Node 的場景,在處理 Request 時可以對 port 進行監聽,並以非同步的方式進行處理而不造成阻塞,並且不用因為大量的請求而佔用過多資源,因為基本上這些請求是共用資源的。
然而單執行緒意味著我們無法善用多 CPU 的資源及優勢,這樣顯然是無法滿足我們的!
不過這部分像是 cluster、PM2、forever 等,其實有許多的解法,在後面的文章會做介紹。
下一章,來提提幫助 node 完成非同步的核心機制 Event Loop。