淺談JS和Nodejs中的事件驅動
事件驅動和發佈-訂閱
事件驅動架構是建立在軟件開發中一種通用模式上的,這種模式被稱為發佈-訂閱或觀察者模式。
在事件驅動架構中,至少有兩個參與者:主題(subject)和觀察者(observer)。
主題就像調頻收音機一樣,向有興趣收聽該主題所說內容的觀察者進行廣播。
觀察者可能隻有一個,也可能有一百個,這都沒有關系,隻要主題有一些要廣播的消息就夠瞭。
請記住,事件驅動、發佈-訂閱和觀察者模式在實踐中不是一回事,但在理想情況下,它們使用相同的方法:一個實體廣播一條消息,其他實體偵聽該消息。
發佈-訂閱模式和我一樣老。在 1987 年左右開始理論化,而觀察者模式則出現在 1994 年由“四人幫”所寫的著作《設計模式》中。
事件驅動是怎樣用在瀏覽器中的JavaScript的?
借助引擎,JavaScript可以運行在你的瀏覽器中。
最受歡迎的 JavaScript 引擎是 Google Chrome 和 Node.js所使用的V8,Firefox 的 SpiderMonkey 和 Safari/WebKit 使用的 JavaScriptCore。
基於供豐富的環境,JavaScript 引擎增強瞭語言,還提供瞭事件驅動的 JavaScript 平臺。
實際上,瀏覽器中的 JavaScript 可以與html元素進行交互,這些html元素是事件發送器(event emitters),即能夠發送事件的對象。
思考一下這個簡單的例子,一個帶有按鈕的 HTML 文檔:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>What means "event-driven" in JavaScript?</title> </head> <body> <div> <button id="subscribe">SUBSCRIBE</button> </div> </body> </html>
如果沒有 JavaScript,則這個按鈕將毫無生命。現在 HTML 按鈕是HTMLButtonElement類型的元素,並且與所有 HTML 元素一樣,它們都連接到EventTarget—— 每個 HTML 元素的共同祖先。
瀏覽器中的事件目標是能夠發出事件的對象:它們是觀察者模式中的主題。
有點混亂?請記住:主題是 FM 廣播,所以任何 HTML 元素都像是廣電臺。
一會兒,你將看到誰是觀察者。
瀏覽器中的主題和觀察者
如果 HTML 元素是主題,那麼誰是觀察者?任何註冊為偵聽器的 JavaScript函數都可以對瀏覽器中的事件做出反應。
使用 JavaScript 選擇一個 HTML 元素:
const btn = document.getElementById('subscribe');
並使用 addEventListener註冊偵聽器:
const btn = document.getElementById('subscribe'); btn.addEventListener("click", function () { console.log("Button clicked"); });
這裡的“click”是事件,按鈕是主題,或者是發送器,函數是偵聽器,或者是觀察者。
回顧一下:
HTML 元素是事件發送器。
JavaScript 中註冊為偵聽器的函數是觀察者。
所有這些組件構成瞭“一個小小的事件驅動的體系結構。要測試代碼請保存下面的 HTML 內容到文件(或在 Codepen 上嘗試),請單擊按鈕,然後查看瀏覽器的控制臺:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>What means "event-driven" in JavaScript?</title> </head> <body> <div> <button id="subscribe">SUBSCRIBE</button> </div> </body> <script> const btn = document.getElementById('subscribe'); btn.addEventListener("click", function () { console.log("Button clicked"); }); </script> </html>
在下一部分中,你將看到用於 Node.js的相同概念。
事件驅動如何用於 Node.js?
Node.js是用於基於 V8 引擎的運行在瀏覽器之外(命令行工具和服務器端)的 JavaScript 環境。
你在 Node.js 中所做的大部分工作都是基於事件的。總會有一個發送器對象,一些觀察者在監聽消息。
在 Node.js 中,沒有任何 HTML 元素,因此大多數事件都來自進程、與網絡的交互、文件等。
Node.js 中的每個事件發送器都有一個名為on的方法,該方法至少需要兩個參數:
- 要偵聽的事件的名稱
- 監聽器函數
讓我們舉一個實際的例子。看一下這個簡單的 Node.js 服務器:
const net = require("net"); const server = net.createServer().listen(8081, "127.0.0.1"); server.on("listening", function () { console.log("Server listening!"); }); server.on("connection", function (socket) { console.log("Client connected!"); socket.end("Hello client!"); });
這段代碼創建瞭一個監聽本地主機端口 8081 的服務器。在server 對象上,我們調用 on 方法來註冊兩個偵聽器函數。
服務器啟動後立即觸發listening事件,而客戶端連接到 127.0.0.1:8081 時將觸發connection 事件(嘗試一下!)。
在此示例中,server是事件發送器,主題。另一方面,偵聽器函數是觀察者。
但是那些on方法從哪裡來的呢?
瞭解 EventEmitter
Node.js 中的所有事件驅動模塊都擴展瞭一個名為EventEmitter的根類。在我們之前的例子中,來自 net 模塊的網絡服務器就使用瞭 EventEmitter。
Node.js 中的EventEmitter有兩種基本方法:on和emit。
如果你想要與瀏覽器對應,那麼可以把EventEmitter看作是能夠發出事件的任何一種 HTML 元素。
要在瀏覽器中偵聽事件,請在主題對象上調用addEventListener:
const btn = document.getElementById('subscribe'); btn.addEventListener("click", function () { console.log("Button clicked"); });
相反,在 Node.js 中有on:
// omit server.on("listening", () => { console.log("Server listening!"); }); // omit
準確地說,EventEmitter上還有一個addListener方法。on是它的別名。
EventEmitter還有一個emit方法,在你廣播自定義事件(消息)時很有用。
如果要使用EventEmitter,請從 “events” 模塊中導入並發出事件:
const EventEmitter = require("events"); const emitter = new EventEmitter(); emitter.on("customEvent", () => console.log("Got event!")); emitter.emit("customEvent");
用 Node.js 運行代碼,你將在控制臺中看到 “Got event”。
JavaScript 中有關觀察者/發佈-訂閱的其他示例
JavaScript 沒有對觀察者對象的原生支持,但是有人建議將其添加到語言中。
RxJS是一個將觀察者模式引入 JavaScript 的庫。
Redux是 JavaScript 中發佈-訂閱模式的實現。 這是一個非常好的事件發送器,其中狀態的更改會被分發給所有監聽的觀察者。
現代瀏覽器附帶Intersection Observer API,這是觀察者模式的另一個例子。
Socket.IO是一個庫,大量使用瞭事件。
總結
希望你從這篇文章中學到新的東西。你學到瞭很多術語,但最終都歸結為大約 30 年前發明的模式:發佈-訂閱。
這種模式,也稱為觀察者,是我們今天在 JavaScript 和 Node.js 中所使用的事件驅動架構的基礎。
再次強調,事件驅動、發佈-訂閱和觀察者的模式並非完全相同:事件驅動的體系結構建立在發佈-訂閱之上,觀察者模式比 DOM 和 Node.js 事件更豐富。
但他們都是屬於同一個傢庭的成員。
以上就是淺談JS和Nodejs中的事件驅動的詳細內容,更多關於JS和Nodejs中的事件驅動的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- nodejs結合Socket.IO實現websocket即時通訊
- Node.js用Socket.IO做聊天軟件的實現示例
- 基於事件冒泡、事件捕獲和事件委托詳解
- JavaScript事件的委托(代理)的用法示例詳解
- 一文搞懂如何避免JavaScript內存泄漏