Evented I/O for V8 JavaScripthtml
事件機制的實現前端
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這些業務事件點便可,至於內部的流程如何,無需過於關注。node
emitter.setMaxListeners(0);
|
繼承event.EventEmittergit
1 function Stream() { 2 events.EventEmitter.call(this); 3 } 4 util.inherits(Stream, events.EventEmitter);
Node.js在工具模塊中封裝了繼承的方法,因此此處能夠很便利地調用。程序員能夠經過這樣的方式輕鬆繼承EventEmitter對象,利用事件機制,能夠幫助你解決一些問題。程序員
多事件之間協做github
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服務器。編程
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的思路保持契合,並能夠適用在前端中。api
利用事件隊列解決雪崩問題緩存
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)移除掉警告,或者設更大的警告閥值。