-
Notifications
You must be signed in to change notification settings - Fork 29.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EventEmitter API #1817
Comments
From #1785 (comment) discussion: |
Existing discussion about EventEmitter#listenerCount: #734 |
Actually, it does. Either the usage of |
@ChALkeR It doesn't block changing the API, it blocks changing |
@chrisdickinson Ok, I might have misphrased that. I meant the internal details of the If you don't want to expose an API to do what But I see nothing wrong in adding an API endpoint that allows getting a list of all events that have listeners (see 1.). |
It's a cowpath, for better or worse. It'd be interesting to get some metrics on how pervasive its use is in the ecosystem, but given readable-stream's dependency on it I'm inclined to say it's probably worth preserving (vs. patching out of the ecosystem.) Updating all libraries dependent on readable-stream of any version would be a pretty intense exercise. With regards to "getting a list of event topic names with listeners" from an EventEmitter – outside of Ultron, are there any other modules that make use of |
@ChALkeR Reason it wasn't placed on the prototype is because EE is inherited by everything, and it was decided at the time that adding a new method could break compatibility. (IIRC there was a module pointed out that uses that same method name) |
A possible way around that is a non-prototype method like
|
I showed up in nodejs/io.js to grieve about not having a real way to do @ChALkeR's #1 use case. I had written up streaming-heart-mother to kind-of/sort-of work around this: it listens to newListener events and exposes an enumeration of them via a This has been a very longstanding issue I've had with the DOM's EventTarget interface, and which EventEmitter repeated- the un-introspectability, the inability to see what's happening on the target site without getting there first and monkey-patching the hay out of it to keep a record for yourself. A good, reflective first-class system would put this right and expose the key information about the object, not retain it from the programmers: looking at _events is a completely incorrect hack that is just a faster, better, clearner version of (my, others) filthy monkey patching. (There's The EventEmitter.prototype API ought be amended so differing implementations of events can successfully cooperate together. _events was never specified, and isn't the right interface- EventEmitter.prototype.getEventNames feels to me much closer to the desired interface. Doing It's high time EventTarget's original high treason against good JS object-ness be corrected. |
There is still one use case I'd like to be able to solve- both dynamic writers and dynamic listeners. For example: if there's a emitter that knows how to read from a variety of kafka queues, and listeners that might want to pick certain queues (listen to events) if the name matches some predicate, then both sides are waiting for the other to either emit or listen, and neither knows whether to tentatively begin bindings/offering itself first. It's a wait-locked situation. It's "solvable" by having the emitter either once or periodically emit zero-sized content events, by being proactive, and letting monkey-patch logic let the listener-side see these emits happening, but just as So in contrast to my Streaming Heart Mother v1.1, which can convey knownEvents on the emitter, a fantastic, out of this world awesome api would be able to let both producers find all listeners, and listeners explore all possible emits systems. It's a dual that I'd like to see filled, of allowing both sides of the emitter to express what capabilities are about (possible emitters, as well as listed listeners). But I get that perhaps this might better be reserved for a higher level collaboration api. |
About (3), there are modules for that:
Maybe the |
@rektide If we added such a reflection API, we'd likely put it on the constructor itself (a la @ChALkeR Even if we fix |
@chrisdickinson Shimming But to remove the bloat in the long-term, usage of Maybe if we do that now and if Edit: There is another PR that breaks the internal |
I will always prefer internal "private" objects over API methods for code that runs in hot path. But given the fact that there are a lot of modules (public and private) that are already using |
@3rd-Eden, |
Trying again. Whatever member getEventNames or _events (which would be fine, if specified)- whether it's on the prototype or publicly on the Object, is fine. So long as there's something formal specified accessible from the Object that people can use to lookup events, it's all good. (And looking at the api docs I do now notice that most methods are held by the emitter, not the EventEmitter.protoype; that's fine- my mistake asking for anything on prototypes previously). I do think a getEventNames would be ideal to standardize around; it plus the existing listeners(event) is sufficient to read out all data from _events, be it a Object or Map, no? That's what make what seems like a good pair to me; it is the most minimal API addition which will yield up the data we see existing use cases relying on. Map#get : Map#keys :: Emitter#listeners : Emitter#getEventNames |
Then don't turn it in a getter/setter... Arnout Kazemier
|
@3rd-Eden Alternatives? |
What's wrong with turning The real events object/array can then be hidden behind an ES6 symbol and, after some time has passed, be changed into a Map or whatever if that is beneficial. Everyone wins, right? |
Sounds perfect to me. @3rd-Eden proposed just the opposite thing in #1817 (comment), and I see no advantages in that approach ☺. |
This is a real capability that people should have @bnoordhuis! @ChALkeR has taken the time to carefully show a variety of real use cases. Kindly make something usable for people with legitimate use cases that won't spew errors! I dunno- make a canonical Introducing more jank because people HAD to use a janky internal feature and calling this done: please please no. I understand the desire to let things settle some before starting to make public apis. A |
Principle of proportionality: if a handful of people are inconvenienced by something that benefits everyone else, it's usually a worthwhile tradeoff. (Although if you take that brand of utilitarianism to its logical extreme, you arrive at fifty years of torture.) The tradeoff here is between the people that use Okay, small digression aside, the request is for something that returns all events and all listeners? Or just event names? My initial suggestion would be to change |
@3rd-Eden Are there use cases in existing modules, that you know of, where operations are performed directly on |
The current problem is that an internal
(2,3) could be solved by using a getter-setter based shim for (1) could be solved by a new API method. It doesn't really matter if that would be inside the protoype (like (4) Probably doesn't need any new API methods because overshadow-listeners or similar techniques look fine. In fact, I propose using the same method in |
@ChALkeR On (1), a new API method- Migrating to an API which can not be recreated outside of Node would be brutal- a big ole wrapping up of the thing of barb wire to those who have regard for isomorphism, and respect javascript as a fine predicate-based loose-coupled language. |
Re: the {g,s}etter approach – I'd advise against making it emit warnings for now. Re: a new API for getting a list of event topics – I'm all for adding that API to the constructor. Similar APIs have been shimmed by browserify, so there's no worry about loosing isomorphism. Adding the API to the constructor is less likely to break existing code. Avoiding unnecessary (or aesthetic) downstream breakage is our primary concern. |
@chrisdickinson it seems like the worst possible thing imaginable for introducing an api: add a singleton method specific to an implementation where interoperability will be impossible. Citing Browserify is a dodge: Browserify doesn't have an interop challenge, a it only has to make standalone (like Node, self-complete) replacement of Node's api. As I asked yesterday, please tell me how EventEmitter3 and Node's events would interop successfully around a method on the constructor? How would EventEmitter7 interop? The dilemna we're stems from the old, old crime of the DOM API: not allowing EventTarget to be reflected on. Fix our defacto new better more widely adopted EventTarget object, and do an ok job, please. Not by introducing hacks that Node can happen to offer (but no one else will be able to interop with) entirely outside of the emitter object. I don't understand the case for adding a method on the constructor, not the object: I've seen one mention of a performance concern, which I honestly don't begin to grasp, and other than that I see what seems like very smart people agreeing to put it on the constructor. And that terrifies the bejeesus out of me, because me makes me think the devs don't see or have respect as I do for Node as the project defining the Javascript platform (as well as being a great runtime). Fracturing vital APIs between objects and their singleton constructor is way way bad news, as far as platforming goes. Again I ask: how is |
It's not a performance concern. The previous discussion around As it stands, |
@3rd-Eden this post is about adding methods to event emitters. I was referring to that - not to emitting symbols. |
Some relevant history: The This gist of the unsolvable, is that it is impossible to fake listen to the |
One way to hide the internal properties is using WeakMap ( ES6), in this way , for example:
|
Per nodejs#1817, there are many modules that currently abuse the private `_events` property on EventEmitter. One of the ways it is used is to determine if a particular event is being listened for. This adds a simple `listEvents()` method that returns an array of the events with currently registered listeners.
Per #1817, there are many modules that currently abuse the private `_events` property on EventEmitter. One of the ways it is used is to determine if a particular event is being listened for. This adds a simple `eventNames()` method that returns an array of the events with currently registered listeners. PR-URL: #5617 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
A handful of modules (including readable-streams) make inappropriate use of the internal _events property. One such use is to prepend an event listener to the front of the array of listeners. To address part of the issue, this adds a new optional bitwise flag to the addListener/on/once methods that, when set, causes the listener to be prepended. Doc update and test case is included. Fixes: nodejs#1817
A handful of modules (including readable-streams) make inappropriate use of the internal _events property. One such use is to prepend an event listener to the front of the array of listeners. This adds EE.prototype.firstOn() and EE.prototype.firstOnce() methods to add handlers to the *front* of the listener array. Doc update and test case is included. Fixes: nodejs#1817
A handful of modules (including readable-streams) make inappropriate use of the internal _events property. One such use is to prepend an event listener to the front of the array of listeners. This adds EE.prototype.prependListener() and EE.prototype.prependOnceListener() methods to add handlers to the *front* of the listener array. Doc update and test case is included. Fixes: nodejs#1817
A handful of modules (including readable-streams) make inappropriate use of the internal _events property. One such use is to prepend an event listener to the front of the array of listeners. This adds EE.prototype.prependListener() and EE.prototype.prependOnceListener() methods to add handlers to the *front* of the listener array. Doc update and test case is included. Fixes: nodejs#1817 PR-URL: nodejs#6032 Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net>
A handful of modules (including readable-streams) make inappropriate use of the internal _events property. One such use is to prepend an event listener to the front of the array of listeners. This adds EE.prototype.prependListener() and EE.prototype.prependOnceListener() methods to add handlers to the *front* of the listener array. Doc update and test case is included. Fixes: #1817 PR-URL: #6032 Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net>
Moved from #1785 (comment)
Some modules use the internal
_events
object. It's not a good thing, and that probably means thatEventEmitter
is missing some API methods.Also
lib/_stream_readable.js
uses the internal_events
object oflib/events.js
, which is ok, but not very nice. What makes that a bit worse is thatlib/_stream_readable.js
is also packaged as an externalreadable-stream
module.Samples of
_events
usage:if (!dest._events || !dest._events.error)
else if (isArray(dest._events.error))
dest._events.error.unshift(onerror);
dest._events.error = [onerror, dest._events.error];
if (this._events.preamble)
if ((start + i) < end && this._events.trailer)
if (this._events[ev])
if (!boy._events.file) {
for (event in this.ee._events) { if (this.ee._events.hasOwnProperty(event)) {
It looks to me that there should be methods in
EventEmitter
to:Get a list of all events from an
EventEmmiter
that currently have listeners. This is whatultron
module does, and I do not see an API for that. Getting a list of events could also be usable for debugging.Implemented by @jasnell in events: add eventNames() method #5617, will be available in 6.0.
(optional) Check if there are any listeners for a specific event. Like
EventEmitter.listenerCount
but using a prototype, likeEventEmitter.prototype.listeners
but returning just the count. This is what seems to be most commonly used.It has a valid API
EventEmitter.listenerCount
, but why isn't it inside the prototype? That makes it a bit harder to find and that could be the reason behind modules usingthis._events.whatever
to count (or check the presense of) the event listeners.That's since 75305f3. @trevnorris
Solved by events: deprecate static listenerCount function #2349.
(optional) To prepend an event. Does the documentation specify the order in which the events are executed, btw?
This is what the internal
lib/_stream_readable.js
and thereadable-stream
module do.Implemented by @jasnell in events: add ability to prepend event listeners #6032.
The text was updated successfully, but these errors were encountered: