如何正確使用Node.js事件

翻譯:瘋狂的技術(shù)宅
原文:https://medium.freecodecamp.org/how-to-keep-your-javascript-code-simple-and-easy-to-read-bff702523e7c

事件驅(qū)動的編程變得流行之前,在程序內(nèi)部進行通信的標準方法非常簡單:如果一個組件想要向另外一個發(fā)送消息抚垃,只是顯式地調(diào)用了那個組件上的方法叠纷。但是在 react 中用的卻是事件驅(qū)動而不是調(diào)用

事件的好處

這種方法能夠使組件更加分離塞祈。在我們繼續(xù)寫程序時办绝,會識別整個過程中的事件冒黑,在正確的時間觸發(fā)它們,并為每個事件附加一個或多個事件監(jiān)聽器嘁信,這使得功能擴展變得更加容易于样。我們可以為特定事件添加更多的 listener,而不必修改現(xiàn)有的偵聽器或觸發(fā)事件的應用程序部分潘靖。我們所談論的是觀察者模式穿剖。

觀察者模式

設計一個事件驅(qū)動的體系結(jié)構(gòu)

對事件進行識別非常重要,我們不希望最終必須從系統(tǒng)中刪除或替換現(xiàn)有事件卦溢,因為這可能會迫使我們刪除或修改附加到事件上的眾多偵聽器糊余。我的一般原則是僅在業(yè)務邏輯單元完成執(zhí)行時才考慮觸發(fā)事件秀又。

假如你想在用戶注冊后發(fā)送一堆不同的電子郵件。注冊過程本身可能會涉及許多復雜的步驟和查詢贬芥,但從商業(yè)角度來看吐辙,這只是其中的一個步驟。每個要發(fā)送的電子郵件也是單獨的步驟蘸劈。因此昏苏,一旦注冊完成馬上就發(fā)布事件是很有意義的。于是我們附加了多個監(jiān)聽器威沫,每個監(jiān)聽器負責發(fā)送一種類型的電子郵件贤惯。

Node的異步事件驅(qū)動架構(gòu)具有一些被稱為“emitters”的對象。它們發(fā)出命名事件壹甥,這些事件會調(diào)用被稱為“l(fā)istener”的函數(shù)救巷。發(fā)出事件的所有對象都是 EventEmitter 類的實例。使用它句柠,我們可以創(chuàng)建自己的事件:

一個例子

讓我們使用內(nèi)置的 events 模塊(我建議你查看這個文檔:https://nodejs.org/api/events.html)以獲取對 EventEmitter 的訪問權(quán)限。

const EventEmitter = require('events');

const myEmitter = new EventEmitter();

module.exports = myEmitter;

這是我們的服務器端程序的一部分棒假,它負責接收HTTP請求溯职,保存新用戶并發(fā)出事件:

const myEmitter = require('./my_emitter');

// Perform the registration steps

// Pass the new user object as the message passed through by this event.
myEmitter.emit('user-registered', user);

附加一個監(jiān)聽器的單獨模塊:

const myEmitter = require('./my_emitter');

myEmitter.on('user-registered', (user) => {
  // Send an email or whatever.
});

將策略與實現(xiàn)分開是一種非常好的做法。在這種情況下帽哑,策略意味著哪些 listener 訂閱了哪些事件谜酒。實現(xiàn)意味著 listener 自己。

const myEmitter = require('./my_emitter');
const sendEmailOnRegistration = require('./send_email_on_registration');
const someOtherListener = require('./some_other_listener');

myEmitter.on('user-registered', sendEmailOnRegistration);
myEmitter.on('user-registered', someOtherListener);
module.exports = (user) => {
  // Send a welcome email or whatever.
}

這種分離使 listener 也可以被重復使用妻枕,它可以被附加到發(fā)送相同消息的其他事件上(用戶對象)僻族。同樣重要的是 當多個 listener 被附加到單個事件時,它們將按照附加的順序同步執(zhí)行屡谐。因此 someOtherListener 將在 sendEmailOnRegistration 完成執(zhí)行后運行述么。

但是,如果你希望自己的 listener 以異步方式運行愕掏,只需用 setImmediate 包裝它們的實現(xiàn)度秘,如下所示:

module.exports = (user) => {
  setImmediate(() => {
    // Send a welcome email or whatever.
  });
}

讓你的 Listeners 保持簡潔

在寫 listener 時要堅持單一責任原則。一個 listener 應該只做一件事并把事情做好饵撑。例如:要避免在 listener 中編寫太多的條件并根據(jù)事件傳來的數(shù)據(jù)(消息)去決定做什么剑梳。在這種情況下使用不同的事件會更加合適:

const myEmitter = require('./my_emitter');

// Perform the registration steps

// The application should react differently if the new user has been activated instantly.
if (user.activated) {
  myEmitter.emit('user-registered:activated', user);
  
} else {
  myEmitter.emit('user-registered', user);
}
const myEmitter = require('./my_emitter');
const sendEmailOnRegistration = require('./send_email_on_registration');
const someOtherListener = require('./some_other_listener');
const doSomethingEntirelyDifferent = require('./do_something_entirely_different');


myEmitter.on('user-registered', sendEmailOnRegistration);
myEmitter.on('user-registered', someOtherListener);

myEmitter.on('user-registered:activated', doSomethingEntirelyDifferent);
view raw

必要時明確分離 Listener

在前面的例子中,我們的 listener 是完全獨立的函數(shù)滑潘。但是在 listener 與對象關(guān)聯(lián)的情況下(這時是一種方法)垢乙,必須手動將其從已訂閱的事件中分離出來。否則對象將永遠不會被垃圾回收语卤,因為對象( listener )的一部分將會繼續(xù)被外部對象( emitter )引用追逮,所以存在內(nèi)存泄漏的可能酪刀。

例如,如果我們正在開發(fā)一個聊天程序羊壹,并且希望當新消息到達用戶進入的聊天室時蓖宦,顯示通知的功能應該位于該用戶對象本身的內(nèi)部,我們可能會這樣做:

class ChatUser {
  
  displayNewMessageNotification(newMessage) {
    // Push an alert message or something.
  }
  
  // `chatroom` is an instance of EventEmitter.
  connectToChatroom(chatroom) {
    chatroom.on('message-received', this.displayNewMessageNotification);
  }

  disconnectFromChatroom(chatroom) {
    chatroom.removeListener('message-received', this.displayNewMessageNotification);
  }
}

當用戶關(guān)閉他的標簽或暫時斷開互聯(lián)網(wǎng)連接時油猫,我們可能希望在服務器端發(fā)起一個回調(diào)稠茂,通知其他用戶有人剛剛下線。當然在這時為脫機用戶調(diào)用 displayNewMessageNotification 沒有任何意義情妖。除非我們刪除它睬关,否則它將繼續(xù)被用于調(diào)用新消息。如果不這樣做毡证,除了不必要的調(diào)用之外电爹,用戶對象也會被永久地保留在內(nèi)存中。因此在用戶脫機時應該在服務器端回調(diào)中調(diào)用 disconnectFromChatroom料睛。

注意事項

如果不小心丐箩,即便是松散耦合的事件驅(qū)動架構(gòu)也會導致復雜性的增加,可能會導致在系統(tǒng)中跟蹤依賴關(guān)系變得很困難恤煞。如果我們從偵聽器內(nèi)部發(fā)出事件屎勘,程序會特別容易出現(xiàn)這類問題。這可能會觸發(fā)意外的事件鏈居扒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末概漱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子喜喂,更是在濱河造成了極大的恐慌瓤摧,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玉吁,死亡現(xiàn)場離奇詭異照弥,居然都是意外死亡,警方通過查閱死者的電腦和手機诈茧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門产喉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人敢会,你說我怎么就攤上這事曾沈。” “怎么了鸥昏?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵塞俱,是天一觀的道長。 經(jīng)常有香客問我吏垮,道長障涯,這世上最難降的妖魔是什么罐旗? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮唯蝶,結(jié)果婚禮上九秀,老公的妹妹穿的比我還像新娘。我一直安慰自己粘我,他們只是感情好鼓蜒,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著征字,像睡著了一般都弹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匙姜,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天畅厢,我揣著相機與錄音,去河邊找鬼氮昧。 笑死框杜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的袖肥。 我是一名探鬼主播霸琴,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昭伸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起澎迎,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤庐杨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夹供,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灵份,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年哮洽,在試婚紗的時候發(fā)現(xiàn)自己被綠了填渠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸟辅,死狀恐怖氛什,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匪凉,我是刑警寧澤枪眉,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站再层,受9級特大地震影響贸铜,放射性物質(zhì)發(fā)生泄漏堡纬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一蒿秦、第九天 我趴在偏房一處隱蔽的房頂上張望烤镐。 院中可真熱鬧,春花似錦棍鳖、人聲如沸炮叶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悴灵。三九已至,卻和暖如春骂蓖,著一層夾襖步出監(jiān)牢的瞬間积瞒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工登下, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茫孔,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓被芳,卻偏偏與公主長得像缰贝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子畔濒,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容