I currently have the problem that I have an object which should listen on another object.
The question is: how should I handle the subscription? Currently, I only know of two possible ways:
With registrator:
var Registrator = function() {
this.listener = new Listener();
};
Registrator.prototype.register = function(subsriber) {
var self = this;
subscriber.on('event', function(info) {
self.listener.doSomething(info);
});
};
In constructor:
var Subscriber = function() {
var listener = require('./instanced-listener');
this.on('event', function(info) {
listener.doSomething(info);
});
};
As you can see, both methods are not that neat. Is there another pattern which I could use for this problem?
PS:
If the example looks too abstract, here is the source:
https://github.com/bodokaiser/documents/blob/master/lib/repository.js
Check the constructor to see my unhappiness :)
Any listeners for the error event should have a callback with one argument to capture the Error object and gracefully handle it. If an EventEmitter emits an error event, but there are no listeners subscribed for error events, the Node. js program would throw the Error that was emitted.
Using async functions with event handlers is problematic, because it can lead to an unhandled rejection in case of a thrown exception: const ee = new EventEmitter(); ee. on('something', async (value) => { throw new Error('kaboom'); });
Since Node. js applications are event-driven, the events are triggered on its associated event handler. An event handler is nothing but a callback function called when an event is triggered. The main loop listens to the event triggers and then calls the corresponding event handler.
It really depends on how the objects will broadcast and listen and how coupled or decoupled you require them to be. Generally speaking, the best use of this type of event architecture is where an object can indiscriminately announce events to other objects and those objects can choose to listen (register) or stop listening (unregister) at their own discretion.
It might make sense in some scenarios to make the listener a dependent of the broadcaster, in which case it will be strongly coupled to the event name and the arguments of the event being announced by that broadcaster.
var Listener = function(broadcaster) {
var self = this;
broadcaster.on("event", function(info) {
self.doSomething(info);
});
};
Other listener classes can also be created and register themselves with the same broadcaster. The broadcaster has no knowledge of the listeners only the listener is aware of the relationship.
The broadcaster announces events only for the listener. In this scenario the event architecture may be overkill, you could replace the events with direct calls. Your second example hints at this because of the one to one relationship with the listener in the broadcaster's constructor.
var Broadcaster = function(listener) {
this.doSomething = function() {
// do something...
listener.doSomething(info);
};
};
This creates strong coupling between the broadcaster and the interface for the listener. This pattern is not as limiting as it may first appear though. A common approach to extending the usefulness of this pattern is to replace the basic listener with a broadcasting adapter. The broadcasting adapter appears the same as the basic listener in that it carries the same doSomething
interface but implements an event architecture to pass this call on to other objects. This keeps the broadcaster very simple and externalizes anything to do with events at the cost of an additional class.
Your first example is a good example of releasing this coupling the cost is an additional class. The intermediate class acts as a bridge or adapter between the two classes so that neither is dependent on each other's information. The event signature is hidden from the listener and the method signature for the listener is hidden from the broadcaster. Often this level of decoupling is unnecessary but is an important tool to be aware of where it is important to keep two classes isolated from each other.
var Bridge = function(listener, broadcaster) {
broadcaster.on("event", function(info) {
// if necessary translate the arguments as well
listener.doSomething(info);
});
};
Per your comment, I'm not sure what you find unreadable about this, but you can try using Stream
objects and the elegant Stream#pipe
method.
var Stream = require('stream').Stream;
var util = require('util');
var Sender = function() {
this.readable = true;
this.saySomething = function(message) {
this.emit('data', message);
};
};
util.inherits(Sender, Stream);
var Receiver = function() {
this.writable = true;
this.write = function(message) {
console.log('received: ' + message);
}
};
util.inherits(Receiver, Stream);
// instance of each
var s = new Sender();
var r = new Receiver();
// connect using pipe!
s.pipe(r);
// an event
s.saySomething('hello world'); //=> received: hello world
I particularly like using Stream
objects. The pipe
method is quite handy :) Each stream has a single input and output. I like them because it encourages you to build small, functional components that are responsible for a specific task. Then, you can chain/reuse them however you see fit.
You'll want to use EventEmitter
Here's a quick example
var events = require('events');
var util = require('util');
var SampleEmitter = function() {
events.EventEmitter.call(this);
this.sayHello = function() {
this.emit('hello', 'hello world');
};
};
util.inherits(SampleEmitter, events.EventEmitter);
var SampleListener = function(emitter) {
emitter.on('hello', function(data) {
console.log(data);
});
};
var se = new SampleEmitter();
var sl = new SampleListener(se);
se.sayHello(); //=> 'hello world'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With