iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
2
Modern Web

前端三十 - 成為更好的前端工程師系列 第 25

25. [BE] Node.js 與 JavaScript 的關係是什麼?

  • 分享至 

  • xImage
  •  

node

隨著 Node.js 的問世,前端開發者的觸角也逐漸蔓延到後端,甚至透過 Electron.js 這類強大的套件,也可以製作出完整的桌面 GUI 應用程式;藉由 Node.js,前端開發者得以使用較為熟悉的 JavaScript 為敲門磚,逐步的拓展自己的技術守備範圍。但為什麼為了網頁而生的語言可以透過 Node.js 跑在伺服器端呢?要解開這個問題,就得從認識 Node.js 出發。

本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您 前往購買 支持!

Node.js

Node.js 是什麼呢?根據官網的說法:

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

「runtime」 指的是執行環境,就如同網頁上的 JavaScript 是在瀏覽器的 JavaScript 引擎上執行,Node.js 就是一個能執行 JavaScript 的環境,而 V8 則是主流瀏覽器 - Google Chrome 的 JavaScript 引擎,負責解析、執行 JavaScript,也就是負責實踐 ECMAScript 規範中定義的部份;另外,V8 是開源的專案,有興趣的讀者可以參照 Google Git - V8

Node.js 以 V8 為核心,加上一系列 C/C++ 的套件,成功的讓 Server 端也可以執行 JavaScript。

node-js-architecture

優點

但是,後端語言已經這麼多了,為什麼還要大費周章的將 JavaScript 移植到 Server 端呢?

這是因為 JavaScript 是一個事件驅動的語言,透過事件迴圈,能讓執行緒幾乎不會被卡住;而這樣的特性,非常適合用來接收高併發(High Concurrency)的請求。

例如在傳統的伺服器中,每個使用者的連接都會產生一個新的執行緒(看實作,不一定),並佔據一定的效能,伺服器在高併發的情況下,很容易就會由於應接不暇而無法服務新的流量;但 Node.js 會將每個 request 變成事件迴圈中待處理的事件,主執行緒只負責承接、轉拋、回應,並持續的在事件迴圈中循環,一切都以事件為核心在驅動程式運行,自然也就不會出現執行緒卡死的現象。

當然,如果是商業邏輯複雜的後端程式,效能瓶頸不在流量的服務,Node.js 就無用武之地;但在設計需要承接高流量,且處理邏輯不太複雜時,Node.js 可能就會是個可以考慮的選項。

功能

前面提到,Node.js 就是一個可以執行 JavaScript 的環境,而這個環境除了提供瀏覽器 Web API 實作的 setTimeoutsetIntervalconsole 之外,也因為執行環境不同,有另外一系列的 API 供開發者使用,例如可以讀寫檔案的 fs、處理網路請求的 http、做加解密雜湊處理的 crypto、設定叢集的 cluster 等等。

詳細的使用說明,可以參考 Node.js 的官網文件

事件迴圈

由於 JavaScript 擁有單執行緒的特性,且為了讓執行緒不會被需要等待的同步程式卡住,必須透過事件迴圈的機制來實現這個目標。如果各位讀者還有印象,我們在 這篇 有聊過瀏覽器中 JavaScript 的事件迴圈,其中有很大一部分是由瀏覽器完成的;在 Node.js 中則透過 libuv 來實現這部分的機制。不同於瀏覽器的事件迴圈,Node.js 中的事件迴圈大致會有以下幾個階段:

  • timers:執行 setTimeout setInterval 給的 callback
  • pending callbacks:執行被延遲到下一個事件迴圈的 I/O Callback
  • idle, prepare:Node.js 內部專用的階段
  • poll:檢索新的 I/O events,執行 I/O callbacks
  • check:執行 setImmediate 給的 callback。
  • close callbacks:執行關閉資源的 callback,例如 socket.on('close', ...)

相對於瀏覽器的事件迴圈多了好多個階段,但其實只是把所有的 callback 分成了四種:timersI/O eventsimmediatesclose handlers,並依照順序輪流執行,其他在概念上還是一樣的:每個階段有自己的 Queue,輪到它時清空 Queue,到下個階段,周而復始。

為避免主執行緒阻塞,poll 階段可以設定執行上限,到達上限時就會將 Queue 內的東西移交到 pending callbacks 階段的 Queue 中,下一個事件迴圈時再接續執行。

比較需要注意的地方是,微任務佇列(microtask queue)在每個階段結束後都會執行、清空,順序是先清空 process.nextTick 的 callback,再執行其他的如 Promise 的 callback。

常有人誤解 process.nextTick,會想問例如「一個 Tick 是多久?」之類的問題,但其實 Tick 指的就是事件迴圈中的一個階段,因此時間是不固定的喔!

結語

今天我們從最基本的介紹出發,認識了 Node.js 這個 Server 端的 JavaScript 執行環境,並提到 Node.js 的語言特性在高併發情況的優勢,最後重點理解 JavaScript 的重要特色:事件迴圈,背後是如何在伺服器端執行的。

今天的 Node.js 介紹就先到這邊吧,明天我們繼續~

參考資料

筆者

Gary

半路出家網站工程師;半生熟的前端加上一點點的後端。
喜歡音樂,喜歡學習、分享,也喜歡當個遊戲宅。

相信一切安排都是最好的路。


上一篇
24. [BE] 請說明一下 npm 的套件管理機制。
下一篇
26. [BE] API 設計拿資料要透過 POST,會有什麼問題嗎?
系列文
前端三十 - 成為更好的前端工程師31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言