by Usama Ashraf
通過Usama Ashraf
如何正確使用Node.js中的事件 (How to use events in Node.js the right way)
Before event-driven programming became popular, the standard way to communicate between different parts of an application was pretty straightforward: a component that wanted to send out a message to another one explicitly invoked a method on that component. But event-driven code is written to react rather than be called.
在事件驅動的編程流行之前,在應用程序的不同部分之間進行通信的標準方法非常簡單:一個組件希望向另一個發出消息,而該組件顯式調用了該組件上的方法。 但是事件驅動的代碼是為了響應而不是被調用而編寫的。
比賽的好處 (The Benefits Of Eventing)
This approach causes our components to be much more decoupled. As we continue to write an application, we identify events along the way. We fire them at the right time and attach one or more event listeners to each one. Extending functionality becomes much easier. We can add on more listeners to a particular event. We are not tampering with the existing listeners or the part of the application where the event was fired from. What we’re talking about is the Observer pattern.
這種方法使我們的組件更加分離 。 在繼續編寫應用程序的過程中,我們一路確定事件。 我們在適當的時間解雇它們,并將一個或多個事件偵聽器附加到每個偵聽器 。 擴展功能變得容易得多。 我們可以為特定事件添加更多的偵聽器。 我們不會篡改現有的偵聽器或觸發事件的應用程序部分。 我們正在談論的是觀察者模式。
設計事件驅動的體系結構 (Designing An Event-Driven Architecture)
Identifying events is pretty important. We don’t want to end up having to remove/replace existing events from the system. This might force us to delete/modify any number of listeners that were attached to the event. The general principle I use is to consider firing an event only when a unit of business logic finishes execution.
識別事件非常重要。 我們不想最終不得不從系統中刪除/替換現有事件。 這可能迫使我們刪除/修改附加到該事件的任意數量的偵聽器。 我使用的一般原則是僅在業務邏輯單元完成執行時才考慮觸發事件。
So say you want to send out a bunch of different emails after a user’s registration. Now, the registration process itself might involve many complicated steps, and queries. But from a business point of view, it is one step. And each of the emails to be sent out are individual steps as well. So it would make sense to fire an event as soon as registration finishes. We have multiple listeners attached to it, each responsible for sending out one type of email.
假設您要在用戶注冊后發送大量不同的電子郵件。 現在,注冊過程本身可能涉及許多復雜的步驟和查詢。 但是從業務角度來看,這只是一步。 而且,要發送的每封電子郵件都是單獨的步驟。 因此,在注冊完成后立即觸發事件是很有意義的。 我們具有多個偵聽器,每個偵聽器負責發送一種電子郵件。
Node’s asynchronous, event-driven architecture has certain kinds of objects called “emitters.” They emit named events which cause functions called “listeners” to be invoked. All objects that emit events are instances of the EventEmitter class. Using it, we can create our own events:
Node的異步事件驅動架構具有某些稱為“發射器”的對象。 它們發出命名事件,這些事件導致稱為“偵聽器”的函數被調用。 所有發出事件的對象都是EventEmitter類的實例。 使用它,我們可以創建自己的事件:
一個例子 (An Example)
Let’s use the built-in events module (which I encourage you to check out in detail) to gain access to EventEmitter
.
讓我們使用內置的事件模塊(我建議您詳細檢查該模塊)來訪問EventEmitter
。
This is the part of the application where our server receives an HTTP request, saves a new user and emits an event:
這是服務器接收HTTP請求,保存新用戶并發出事件的應用程序的一部分:
And a separate module where we attach a listener:
還有一個單獨的模塊,我們在其中附加了一個偵聽器:
It’s a good practice to separate policy from implementation. In this case policy means which listeners are subscribed to which events. Implementation means the listeners themselves.
將政策與實施分開是一個好習慣。 在這種情況下,策略意味著哪些偵聽器訂閱了哪些事件。 實現是指偵聽器本身。
This separation allows for the listener to become re-usable too. It can be attached to other events that send out the same message (a user object). It’s also important to mention that when multiple listeners are attached to a single event, they will be executed synchronously and in the order that they were attached. Hence someOtherListener
will run after sendEmailOnRegistration
finishes execution.
這種分離也使偵聽器也可以重用。 它可以附加到發出相同消息的其他事件(用戶對象)。 同樣重要的是要提到, 當多個偵聽器附加到一個事件時,它們將按照附加的順序同步執行 。 因此, someOtherListener
將在sendEmailOnRegistration
完成執行之后運行。
However, if you want your listeners to run asynchronously you can simply wrap their implementations with setImmediate
like this:
但是,如果您希望監聽器異步運行,則可以使用setImmediate
包裝它們的實現,如下所示:
保持聽眾干凈 (Keep Your Listeners Clean)
Stick to the Single Responsibility Principle when writing listeners. One listener should do one thing only and do it well. Avoid, for instance, writing too many conditionals within a listener that decide what to do depending on the data (message) that was transmitted by the event. It would be much more appropriate to use different events in that case:
編寫偵聽器時,請遵循“單一責任原則”。 一個聽眾應該只做一件事,并且做好。 例如,避免在偵聽器中編寫太多條件,以根據事件傳輸的數據(消息)來決定要做什么。 在這種情況下,使用不同的事件會更合適:
在必要時明確分離偵聽器 (Detaching Listeners Explicitly When Necessary)
In the previous example, our listeners were totally independent functions. But in cases where a listener is associated with an object (it’s a method), it has to be manually detached from the events it had subscribed to. Otherwise, the object will never be garbage-collected since a part of the object (the listener) will continue to be referenced by an external object (the emitter). Thus the possibility of a memory leak.
在前面的示例中,我們的偵聽器是完全獨立的功能。 但是,如果偵聽器與對象(這是一種方法)相關聯,則必須將其與已訂閱的事件手動分離。 否則,該對象將永遠不會被垃圾回收,因為該對象的一部分(偵聽器)將繼續被外部對象(發射器)引用。 因此存在內存泄漏的可能性。
For example, if we’re building a chat application and we want the responsibility for showing a notification when a new message arrives in a chat room that a user has connected to should lie within that user object itself, we might do this:
例如,如果我們正在構建一個聊天應用程序,并且我們希望當用戶連接到的新消息到達聊天室時,負責顯示通知的責任應該位于該用戶對象本身內,我們可以這樣做:
When the user closes his/her tab or loses their internet connection for a while, naturally, we might want to fire a callback on the server-side that notifies the other users that one of them just went offline. At this point, of course, it doesn’t make any sense for displayNewMessageNotification
to be invoked for the offline user. It will continue to be called on new messages unless we remove it explicitly. If we don’t, aside from the unnecessary call, the user object will also stay in memory indefinitely. So be sure to call disconnectFromChatroom
in your server-side callback that executes whenever a user goes offline.
當用戶關閉其選項卡或暫時失去其互聯網連接時,自然地,我們可能希望在服務器端觸發回調,以通知其他用戶其中一個剛下線。 當然,在這一點上,為脫機用戶調用displayNewMessageNotification
沒有任何意義。 除非我們明確刪除它,否則它將繼續在新消息上被調用。 如果我們不這樣做,除了不必要的調用之外,用戶對象還將無限期地保留在內存中。 因此,請確保在用戶下線時執行的服務器端回調中調用disconnectFromChatroom
。
謹防 (Beware)
The loose coupling in event-driven architectures can also lead to increased complexity if we’re not careful. It can be difficult to keep track of dependencies in our system. Our application will become especially prone to this problem if we start emitting events from within listeners. This could possibly trigger chains of unexpected events.
如果我們不小心,事件驅動的體系結構中的松散耦合也會導致復雜性的增加。 跟蹤我們系統中的依賴關系可能很困難。 如果我們開始從偵聽器內部發出事件,我們的應用程序將特別容易出現此問題。 這可能會觸發一系列意外事件。
翻譯自: https://www.freecodecamp.org/news/using-events-in-node-js-the-right-way-fc50c060f23b/