1. 程式人生 > >Node.js入門:事件機制

Node.js入門:事件機制

Evented I/O for V8 JavaScript

    基於V8引擎實現的事件驅動IO。

事件機制的實現

    Node.js中大部分的模組,都繼承自Event模組(http://nodejs.org/docs/latest/api/events.html )。Event模組(events.EventEmitter)是一個簡單的事件監聽器模式的實現。具有addListener/on,once,removeListener,removeAllListeners,emit等基本的事件監聽模式的方法實現。它與前端DOM樹上的事件並不相同,因為它不存在冒泡,逐層捕獲等屬於DOM的事件行為,也沒有preventDefault()、stopPropagation()、stopImmediatePropagation() 等處理事件傳遞的方法。     從另一個角度來看,事件偵聽器模式也是一種事件鉤子(hook)的機制,利用事件鉤子匯出內部資料或狀態給外部呼叫者。Node.js中的很多物件,大多具有黑盒的特點,功能點較少,如果不通過事件鉤子的形式,物件執行期間的中間值或內部狀態,是我們無法獲取到的。這種通過事件鉤子的方式,可以使程式設計者不用關注元件是如何啟動和執行的,只需關注在需要的事件點上即可。
 1
var options = { 2 host: 'www.google.com', 3 port: 80, 4 path: '/upload', method: 'POST' 5 }; 6 var req = http.request(options, function (res) { 7 console.log('STATUS: ' + res.statusCode); 8 console.log('HEADERS: ' + JSON.stringify(res.headers)); 9 res.setEncoding('utf8');
10 res.on('data', function (chunk) { 11 console.log('BODY: ' + chunk); 12 }); 13 }); 14 req.on('error', function (e) { 15 console.log('problem with request: ' + e.message); 16 }); 17 // write data to request body 18 req.write('data\n'); 19 req.write('data\n'); 20 req.end();

    在這段HTTP request的程式碼中,程式設計師只需要將視線放在error,data這些業務事件點即可,至於內部的流程如何,無需過於關注。

    值得一提的是如果對一個事件添加了超過10個偵聽器,將會得到一條警告,這一處設計與Node.js自身單執行緒執行有關,設計者認為偵聽器太多,可能導致記憶體洩漏,所以存在這樣一個警告。可以將這個限制去掉,呼叫:
emitter.setMaxListeners(0);
    其次,為了提升Node.js的程式的健壯性,EventEmitter物件對error事件進行了特殊對待。如果執行期間的錯誤觸發了error事件。EventEmitter會檢查是否有對error事件新增過偵聽器,如果添加了,這個錯誤將會交由該偵聽器處理,否則,這個錯誤將會作為異常丟擲。如果外部沒有捕獲這個異常,將會引起執行緒的退出。

繼承event.EventEmitter

    實現一個繼承了EventEmitter類是十分簡單的,以下是Node.js中流物件繼承EventEmitter的例子:
1 function Stream() { 
2     events.EventEmitter.call(this); 
3 }
4 util.inherits(Stream, events.EventEmitter);

    Node.js在工具模組中封裝了繼承的方法,所以此處可以很便利地呼叫。程式設計師可以通過這樣的方式輕鬆繼承EventEmitter物件,利用事件機制,可以幫助你解決一些問題。

多事件之間協作

    在略微大一點的應用中,資料與Web伺服器之間的分離是必然的,如新浪微博、Facebook、Twitter等。這樣的優勢在於資料來源統一,並且可以為相同資料來源制定各種豐富的客戶端程式。以Web應用為例,在渲染一張頁面的時候,通常需要從多個數據源拉取資料,並最終渲染至客戶端。Node.js在這種場景中可以很自然很方便的同時並行發起對多個數據源的請求。
1 api.getUser("username", function (profile) { 
2     // Got the profile 
3 }); 
4 api.getTimeline("username", function (timeline) { 
5     // Got the timeline 
6 }); 
7 api.getSkin("username", function (skin) { 
8     // Got the skin 
9 });

    Node.js通過非同步機制使請求之間無阻塞,達到並行請求的目的,有效的呼叫下層資源。但是,這個場景中的問題是對於多個事件響應結果的協調並非被Node.js原生優雅地支援。為了達到三個請求都得到結果後才進行下一個步驟,程式也許會被變成以下情況:

1 api.getUser("username", function (profile) { 
2     api.getTimeline("username", function (timeline) { 
3         api.getSkin("username", function (skin) { 
4             // TODO 
5         }); 
6     }); 
7 });

    這將導致請求變為序列進行,無法最大化利用底層的API伺服器。

    為解決這類問題,有一個模組(EventProxy,https://github.com/JacksonTian/eventproxy)來實現多事件協作,以下為上面程式碼的改進版:
 1 var proxy = new EventProxy(); 
 2 proxy.all("profile", "timeline", "skin", function (profile, timeline, skin) { 
 3    // TODO 
 4 }); 
 5 api.getUser("username", function (profile) { 
 6     proxy.emit("profile", profile); 
 7 }); 
 8 api.getTimeline("username", function (timeline) { 
 9     proxy.emit("timeline", timeline); 
10 }); 
11 api.getSkin("username", function (skin) { 
12     proxy.emit("skin", skin); 
13 });

    EventProxy也是一個簡單的事件偵聽者模式的實現,由於底層實現跟Node.js的EventEmitter不同,無法合併進Node.js中。但是卻提供了比EventEmitter更強大的功能,且API保持與EventEmitter一致,與Node.js的思路保持契合,並可以適用在前端中。

    這裡的all方法是指偵聽完profile、timeline、skin三個方法後,執行回撥函式,並將偵聽接收到的資料傳入。

利用事件佇列解決雪崩問題

    所謂雪崩問題,是在快取失效的情景下,大併發高訪問量同時湧入資料庫中查詢,資料庫無法同時承受如此大的查詢請求,進而往前影響到網站整體響應緩慢。那麼在Node.js中如何應付這種情景呢。
1 var select = function (callback) { 
2     db.select("SQL", function (results) { 
3         callback(results); 
4     }); 
5 };

    以上是一句資料庫查詢的呼叫,如果站點剛好啟動,這時候快取中是不存在資料的,而如果訪問量巨大,同一句SQL會被髮送到資料庫中反覆查詢,影響到服務的整體效能。一個改進是新增一個狀態鎖。

 1 var status = "ready"; 
 2 var select = function (callback) { 
 3     if (status === "ready") { 
 4         status = "pending"; 
 5         db.select("SQL", function (results) { 
 6             callback(results); 
 7             status = "ready"; 
 8         }); 
 9     } 
10 };

    但是這種情景,連續的多次呼叫select,只有第一次呼叫是生效的,後續的select是沒有資料服務的。所以這個時候引入事件佇列吧:

 1 var proxy = new EventProxy(); 
 2 var status = "ready"; 
 3 var select = function (callback) { 
 4     proxy.once("selected", callback); 
 5     if (status === "ready") { 
 6         status = "pending"; 
 7         db.select("SQL", function (results) { 
 8             proxy.emit("selected", results); 
 9             status = "ready";
10         }); 
11      } 
12 };

    這裡利用了EventProxy物件的once方法,將所有請求的回撥都壓入事件佇列中,並利用其執行一次就會將監視器移除的特點,保證每一個回撥只會被執行一次。對於相同的SQL語句,保證在同一個查詢開始到結束的時間中永遠只有一次,在這查詢期間到來的呼叫,只需在佇列中等待資料就緒即可,節省了重複的資料庫呼叫開銷。由於Node.js單執行緒執行的原因,此處無需擔心狀態問題。這種方式其實也可以應用到其他遠端呼叫的場景中,即使外部沒有快取策略,也能有效節省重複開銷。此處也可以用EventEmitter替代EventProxy,不過可能存在偵聽器過多,引發警告,需要呼叫setMaxListeners(0)移除掉警告,或者設更大的警告閥值。

相關推薦

Node.js入門事件機制

Evented I/O for V8 JavaScript     基於V8引擎實現的事件驅動IO。 事件機制的實現     Node.js中大部分的模組,都繼承自Event模組(http://nodejs.org/docs/latest/api/events.html )。Event模組(ev

Node.js入門模組機制

**CommonJS規範 ** 早在Netscape誕生不久後,JavaScript就一直在探索本地程式設計的路,Rhino是其代表產物。無奈那時服務端JavaScript走的路均是參考眾多伺服器端語言來實現的,在這樣的背景之下,一沒有特色,二沒有實用價值。但是隨著JavaScript在前端的應

Node.js入門檔案查詢機制

檔案查詢流程圖 從檔案模組快取中載入     儘管原生模組與檔案模組的優先順序不同,但是都不會優先於從檔案模組的快取中載入已經存在的模組。 從原生模組載入     原生模組的優先順序僅次於檔案模組快取的優先順序。require方法在解析檔名之後,優先檢查模組是否在原生模組列

Node.js入門前後端模組的異同

    通常有一些模組可以同時適用於前後端,但是在瀏覽器端通過script標籤的載入JavaScript檔案的方式與Node.js不同。Node.js在載入到最終的執行中,進行了包裝,使得每個檔案中的變數天然的形成在一個閉包之中,不會汙染全域性變數。而瀏覽器端則通常是裸露的JavaScript程式碼片段。所以

Node.js入門非同步IO

非同步IO     在作業系統中,程式執行的空間分為核心空間和使用者空間。我們常常提起的非同步I/O,其實質是使用者空間中的程式不用依賴核心空間中的I/O操作實際完成,即可進行後續任務。 同步IO的並行模式 多執行緒單程序     多執行緒的設計之處就是為了在共享的程式空間中,實現並行處理

Node.js入門包結構

    JavaScript缺少包結構。CommonJS致力於改變這種現狀,於是定義了包的結構規範(http://wiki.commonjs.org/wiki/Packages/1.0 )。而NPM的出現則是為了在CommonJS規範的基礎上,實現解決包的安裝解除安裝,依賴管理,版本管理等問題。require

Node.js入門Hello World

  馬上開始我們第一個Node.js應用:“Hello World”。開啟你的編輯器,建立一個hello.js檔案。編寫程式碼儲存該檔案,並通過Node.js來執行。 控制檯輸出 1 console.log('hello, nodejs.') ; Web輸出 1

Node.js入門Node.js&NPM的安裝與配置

Node.js安裝與配置      Node.js已經誕生兩年有餘,由於一直處於快速開發中,過去的一些安裝配置介紹多數針對0.4.x版本而言的,並非適合最新的0.6.x的版本情況了,對此,我們將在0.6.x的版本上介紹Node.js的安裝和配置。(本文一律以0.6.1為例,0.6的其餘版本,只需替換版本號即

(原創)node.js入門之一express簡單伺服器搭建-Mac環境開發

0:開篇廢話 好久沒來記錄點東西了,以前記錄的都是一些解決小問題的程式碼片段,只能算是當記事本來用的吧。 換了工作,好像沒那麼多程式碼要寫了,那就自己找點事做,於是重新翻出了nodejs,以前是在主程的搭建環境下,寫一些介面給我的iOS前

Node.js中的事件處理機制

event模組是在Node.js中用以實現各種事件處理的模組。Node.js中的許多模組都集成了event模組,所以event模組是Node.js中一個相當重要的模組。而EventEmitter則是event模組唯一一個對外暴露的物件,主要用於事件的監聽和觸發。所有可能觸發事件的物件都是一個

極簡 Node.js 入門 - 2.2 事件

> 極簡 Node.js 入門系列教程:[https://www.yuque.com/sunluyong/node](https://www.yuque.com/sunluyong/node) > > 本文更佳閱讀體驗:[https://www.yuque.com/sunluyong/node/events]

Node.js入門以及第一個helloworld程序

目錄 rip 工作 直播 需要 減少 web容器 用戶 長連接 1、概念:簡單的說 Node.js 就是運行在服務端的 JavaScript。學之前需要明白Node.js是無法挑戰jsp、php或者asp這種老牌網站的地位的,是永遠不會出現在證券、金融這種領域的。node.

Node.js入門》Windows 7下Node.js Web開發環境搭建筆記

基於 方法 一位 實時 ibm cal 項目 直觀 ear 近期想嘗試一下在IBM Bluemix上使用Node.js創建Web應用程序。所以須要在本地搭建Node.js Web的開發測試環境。這裏講的是Windows下的搭建方法,使用CentOS 的小夥伴請參考:《No

Node.js入門

js代碼 roc 簡單 本地 node span server -s host Node的安裝就不說了,流程見http://www.runoob.com/nodejs/nodejs-install-setup.html。直接來做一個簡單的web服務器 首先輸入並保存一個js

node js實戰帶數據庫,加密的註冊登錄表單

settings else input 是否 rip dig code setting 個人 demo 註冊效果: 登陸效果: 數據庫截圖: 數據庫操作 db.js //這個模塊裏面封裝了所有對數據庫的常用操作 var MongoClient =

Node.js的異步機制的思考

同步 多層 async syn 條件 機制 api 數據庫 詳細 Node.js的異步機制是其最大的特色,異步可以應對高並發,具有很好的性能。 但是如果在某個方法裏,涉及到數據庫的多層查詢,異步機制反而成為阻礙。當執行完第一層SQL後,根據所得的結果集(rows)進行結果集

Node.js入門到企業Web開發中的應用

實戰 tput 統一 sse 介紹 有用 enc oba 入門到 第1章 課程內容介紹主要介紹為什麽我們錄制本次課程、課程包含的主要內容。1-1 導學1-2 課程介紹 第2章 NodeJS 是什麽,為什麽偏愛NodeJS?在一切課程課程內容開始之前先了解一下 NodeJS

易學筆記-第2部分 Node.js入門指南/第5章 Node.js在幣圈流行麼

第2部分 Node.js入門指南/第5章 Node.js在幣圈流行麼/5.1 Node.js在開源社群很流行 Node.js在開源社群很流行 概念:JavaScript大部分專案都是建立在Node.js平臺之上的 在Github上專案採用的語言統計,JavaScr

Node.js入門學習筆記(1)

node.js筆記(1) 前端小白 自己打算做個微信小程式,剛好學習一下node.js,準備用node搭建一個簡單的後臺。寫部落格記錄一下學習進度,另一方面加強對新知識的理解。 主要學習途徑 一、node安裝 之前安裝過,這裡簡單記錄一下 安裝包地址 檢查n

Node js入門 Windows 7下Node js Web開發環境搭建筆記

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!