等待時,最常用的方法就是讓他在同一個thread裡面,函數執行結束後理所當然的就會執行下一步驟。若是用不同thread處理時就無法等待,可能就會用一個旗標(flag)去判斷繼續執行或等待,不過這樣的等待會造成整個thread block住(busy loop),不僅浪費thread資源,也浪費CPU的效能。
事件觸發這個想法也不是從nodejs開始的,很早之前在JAVA、C#和前端javascript就已經開始使用了。但在nodejs中這會是一個絕佳的利器,因為你沒辦法擁有多執行緒,也無法完整的控管已經被分配出去抓不到的非同步函數。
作業系統:linux/windows
版本:nodejs v4.X
主要模組: events
輔助套件:
readline - 講解用,實際上使用並不會用到
util - 繼承用,若物件需要被賦予事件的功能時會用到
名詞
callback/cb - 回呼函數,在註冊事件時,傳入的function,以便事件觸發時將值傳入並執行
emit/trigger - 同樣都是觸發的意思,在不同套件中可能會是其中一個,或是兩個都有。
範例:從console中讀取字串
在C語言裡面我們從用gets或是scanf,但是在執行下去之後C程式會block住,等待使用者輸入訊息。好一點的方法會請kernal去監聽STDIN串流(stream),有值讀才去讀,或是用一個thread去等待,進而不影響到main的執行。
在nodejs中方法和請kernal去監聽有點像,當使用者有輸入訊息時才去處理任務。
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on("line", function(line) {
console.log(line);
});
當rl發生 line 事件時,做第二個參數的事情。事件發生時,是由event emitter替你執行,將值丟入你所給的callback函數中,所以實作event時要對照模組的相關api,才能知道收到的參數是什麼東西。
事件發生的時候,function內部的this會是觸發事件的event emitter,如果你是用繼承的方式,那麼就會是觸發事件的那個object。
註:本篇主要講解event,若對 readline模組有疑問可以看官方說明。
實作event emitter
var EVENT_CLASS = require("events");
var event_object = new EVENT_CLASS();
event_object.on("event name", function(message) {
console.log("event:event name trigger");
console.log(message);
});
event_object.emit("event name", "Hello World");
/**
* output:
* "event:event name trigger"
* "Hello World"
*/
event name是自己定義的,想要叫什麼名字都行,想要監聽多少事件都行,要注意的是多少人去監聽(on)是有限制的,預設值10,如果超過了,程式會丟出例外。
on是監聽事件,第二個參數是發生事件時所要執行的callback。
emit是觸發事件,在發生相關事情的時候主動執行,如果該物件有被監聽觸發的事件,就會將第二個參數之後傳入監聽的callback函數裡頭。
繼承event emitter
如果只是將event emitter實作出來會有點像是從第三方去控管事件,不過當物件多起來的時候這樣會變得很複雜,相同的事件名稱處發在不同的物件上面,很容易造成混淆。
因此你也許希望,每個物件都有自己的事件可以觸發和監聽,那你就必須要到繼承了。
註:詳細的class實作將會在[Nodejs]函數/類別(function)
var EVENT_CLASS = require("events");
var util = require('util');
var object_class = function() {
this.name = "Shach";
this.url = "http://shachkuo.blogspot.tw/";
EVENT_CLASS.call(this);
};
util.inherits(object_class, EVENT_CLASS);
object_class.prototype.show = function() {
console.log("hello " + this.name + ", your blogger url:" + this.url);
};
object_class.prototype.set_url = function(url) {
if (url) {
if (url !== this.url) {
this.url = url;
this.emit("change");
}
}
};
var event_object = new object_class();
event_object.on("change", function() {
console.log("object had change");
});
event_object.show();
event_object.set_url("test change");
event_object.show();
/**
* output:
* hello Shach, your blogger url:http://shachkuo.blogspot.tw/
* object had change
* hello Shach, your blogger url:test change
*/
以上提供的是從官方正統的物件繼承事件功能,不過這種方法Shach本身基本上是完全沒用到,未來會再寫一篇運用於後端的[Nodejs]Backbone(Model/View/Collection),到時在跟大家討論我自己本身如何使用。
可用api
監聽事件:
// 監聽觸發(兩者相同)
event_object.addListener({string event_name}, {function callback});
event_object.on({string event_name}, {function callback});
// 只監聽一次觸發
event_object.once({string event_name}, {function callback});
觸發事件:
event_object.emit({string event_name}, {any_type argument}, {any_type argument}, {any_type argument}, ...);
取得/設訂/查詢:
event_object.getMaxListeners()
event_object.setMaxListeners(n)
event_object.listenerCount(event)
event_object.listeners(event)
移除監聽事件:
// 有輸入event name則移除該event所有監聽的callback
// 沒有輸入則移出該object所有事件callback
event_object.removeAllListeners([event])
event_object.removeListener(event, listener)
沒有留言:
張貼留言