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/