您好,登錄后才能下訂單哦!
如何正確的使用Node.js事件?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。
事件的好處
這種方法能夠使組件更加分離。在我們繼續(xù)寫(xiě)程序時(shí),會(huì)識(shí)別整個(gè)過(guò)程中的事件,在正確的時(shí)間觸發(fā)它們,并為每個(gè)事件附加一個(gè)或多個(gè)事件監(jiān)聽(tīng)器,這使得功能擴(kuò)展變得更加容易。我們可以為特定事件添加更多的 listener,而不必修改現(xiàn)有的偵聽(tīng)器或觸發(fā)事件的應(yīng)用程序部分。我們所談?wù)摰氖怯^(guān)察者模式。
設(shè)計(jì)一個(gè)事件驅(qū)動(dòng)的體系結(jié)構(gòu)
對(duì)事件進(jìn)行識(shí)別非常重要,我們不希望最終必須從系統(tǒng)中刪除或替換現(xiàn)有事件,因?yàn)檫@可能會(huì)迫使我們刪除或修改附加到事件上的眾多偵聽(tīng)器。我的一般原則是僅在業(yè)務(wù)邏輯單元完成執(zhí)行時(shí)才考慮觸發(fā)事件。
假如你想在用戶(hù)注冊(cè)后發(fā)送一堆不同的電子郵件。注冊(cè)過(guò)程本身可能會(huì)涉及許多復(fù)雜的步驟和查詢(xún),但從商業(yè)角度來(lái)看,這只是其中的一個(gè)步驟。每個(gè)要發(fā)送的電子郵件也是單獨(dú)的步驟。因此,一旦注冊(cè)完成馬上就發(fā)布事件是很有意義的。于是我們附加了多個(gè)監(jiān)聽(tīng)器,每個(gè)監(jiān)聽(tīng)器負(fù)責(zé)發(fā)送一種類(lèi)型的電子郵件。
Node的異步事件驅(qū)動(dòng)架構(gòu)具有一些被稱(chēng)為“emitters”的對(duì)象。它們發(fā)出命名事件,這些事件會(huì)調(diào)用被稱(chēng)為“l(fā)istener”的函數(shù)。發(fā)出事件的所有對(duì)象都是 EventEmitter 類(lèi)的實(shí)例。使用它,我們可以創(chuàng)建自己的事件:
一個(gè)例子
讓我們使用內(nèi)置的 events 模塊(我建議你查看這個(gè)文檔:https://nodejs.org/api/events...)以獲取對(duì) EventEmitter 的訪(fǎng)問(wèn)權(quán)限。
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); module.exports = myEmitter;
這是我們的服務(wù)器端程序的一部分,它負(fù)責(zé)接收HTTP請(qǐng)求,保存新用戶(hù)并發(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);
附加一個(gè)監(jiān)聽(tīng)器的單獨(dú)模塊:
const myEmitter = require('./my_emitter'); myEmitter.on('user-registered', (user) => { // Send an email or whatever. });
將策略與實(shí)現(xiàn)分開(kāi)是一種非常好的做法。在這種情況下,策略意味著哪些 listener 訂閱了哪些事件。實(shí)現(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ù)使用,它可以被附加到發(fā)送相同消息的其他事件上(用戶(hù)對(duì)象)。同樣重要的是 當(dāng)多個(gè) listener 被附加到單個(gè)事件時(shí),它們將按照附加的順序同步執(zhí)行。因此 someOtherListener 將在 sendEmailOnRegistration 完成執(zhí)行后運(yùn)行。
但是,如果你希望自己的 listener 以異步方式運(yùn)行,只需用 setImmediate 包裝它們的實(shí)現(xiàn),如下所示:
module.exports = (user) => { setImmediate(() => { // Send a welcome email or whatever. }); }
讓你的 Listeners 保持簡(jiǎn)潔
在寫(xiě) listener 時(shí)要堅(jiān)持單一責(zé)任原則。一個(gè) listener 應(yīng)該只做一件事并把事情做好。例如:要避免在 listener 中編寫(xiě)太多的條件并根據(jù)事件傳來(lái)的數(shù)據(jù)(消息)去決定做什么。在這種情況下使用不同的事件會(huì)更加合適:
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
必要時(shí)明確分離 Listener
在前面的例子中,我們的 listener 是完全獨(dú)立的函數(shù)。但是在 listener 與對(duì)象關(guān)聯(lián)的情況下(這時(shí)是一種方法),必須手動(dòng)將其從已訂閱的事件中分離出來(lái)。否則對(duì)象將永遠(yuǎn)不會(huì)被垃圾回收,因?yàn)閷?duì)象( listener )的一部分將會(huì)繼續(xù)被外部對(duì)象( emitter )引用,所以存在內(nèi)存泄漏的可能。
例如,如果我們正在開(kāi)發(fā)一個(gè)聊天程序,并且希望當(dāng)新消息到達(dá)用戶(hù)進(jìn)入的聊天室時(shí),顯示通知的功能應(yīng)該位于該用戶(hù)對(duì)象本身的內(nèi)部,我們可能會(huì)這樣做:
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); } }
當(dāng)用戶(hù)關(guān)閉他的標(biāo)簽或暫時(shí)斷開(kāi)互聯(lián)網(wǎng)連接時(shí),我們可能希望在服務(wù)器端發(fā)起一個(gè)回調(diào),通知其他用戶(hù)有人剛剛下線(xiàn)。當(dāng)然在這時(shí)為脫機(jī)用戶(hù)調(diào)用 displayNewMessageNotification 沒(méi)有任何意義。除非我們刪除它,否則它將繼續(xù)被用于調(diào)用新消息。如果不這樣做,除了不必要的調(diào)用之外,用戶(hù)對(duì)象也會(huì)被永久地保留在內(nèi)存中。因此在用戶(hù)脫機(jī)時(shí)應(yīng)該在服務(wù)器端回調(diào)中調(diào)用 disconnectFromChatroom。
注意事項(xiàng)
如果不小心,即便是松散耦合的事件驅(qū)動(dòng)架構(gòu)也會(huì)導(dǎo)致復(fù)雜性的增加,可能會(huì)導(dǎo)致在系統(tǒng)中跟蹤依賴(lài)關(guān)系變得很困難。如果我們從偵聽(tīng)器內(nèi)部發(fā)出事件,程序會(huì)特別容易出現(xiàn)這類(lèi)問(wèn)題。這可能會(huì)觸發(fā)意外的事件鏈。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。