-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
events: add ability to prepend event listeners #6032
Conversation
I'm not sure something like this should be documented since the whole point of Readable's use of Yes, it's still possible for people to do this even if left undocumented, but it's less likely to happen and if it does, the end user should know they could end up shooting themselves in the foot. |
Another option: Readable can implement an override for on/once/addListener that emits a process warning if the flag is used to prepend error handlers. |
Or... a similar on/once/addListener override on listener could remove it's listener and prepend it again whenever the flag is used to prepend a new error listener. |
@mscdex Could this be solved by throwing an exception if a listener with |
@ChALkeR That would mean storing more state somehow and AFAIK the way that would have to be implemented (storing flags for existing events in a separate object) would cause noticeable performance regressions. |
I'd considered something along those lines also but I'd prefer not to have
|
-1, it makes no sense to make it public.:
A couple of ideas:
|
Refs: #5833 (comment) -- I think it should be an options object over a flag. |
@vkurchatkin ... I'm fine if this doesn't make it in but the motivation for making this public is to discourage module developers from hacking against internal fields like |
@Fishrock123 ... I'm good with the options object. |
Note: if this ends up internal, we can just use a true/false parameter |
@vkurchatkin We have an external module inside the org atm that misuses the internal API: nodejs/readable-stream. |
@ChALkeR true, not sure what to do with this. Maybe we can just add listener normally? Is there an explanation, why it's really necessary to prepend the listener? |
/cc @nodejs/streams |
so what streams is really trying to do (and other modules might need to do as well) is set an error listener that doesn't prevent the default throwing behavior from also happening (in the case of streams it's to do resource cleanup), some other way we could handle it include
|
Looking at the readable streams case in more detail, the error handler that is being prepended is getting set on the destination object that is being passed to the pipe method. In other words, Readable is modifying the internal private state of an object that it does not own. The only way to do this correctly would be via a public API. I suppose that we could introduce a new |
What about just monkey-patching |
@mscdex ... that seems a bit too hackish also, and given that we've made the decision not to support modules that monkey patch node internals we likely shouldn't be relying on that kind of behavior either. Again, just continuing the brainstorming here: Another thought came to mind: an undocumented const myEE = new EventEmitter();
myEE.on('pre-error', (er) => { /* ... */ });
myEE.on('pre-foo', (a,b,c) => { /* ... */ }); Essentially, prefix any event name with |
@jasnell Seems good to me, but why keep it undocumented? If those events would be added, at some point someone would want to document them. Why not document those from the beginning? |
Personally I'm good with documenting it. I suggested that given the feedback here questioning whether it should be documented. |
For a solution like that I'd be very careful about reserving such a prefix since I could easily see someone already implementing similarly-named events. |
if we were to add a before method to the event object that would also help with versioning, if it was a pre event or an options object there would be no way to know if the event emitter supported that api. |
@mscdex I couldn't find |
@calvinmetcalf makes a very good point. Having something like a |
If we went with e.g. what should happen in the following case: const myEE = new EventEmitter();
myEE.before('foo', () => console.log('a'));
myEE.before('foo', () => console.log('b'));
myEE.on('foo', () => console.log('c'));
myEE.emit('foo'); |
Should we name it something more explicit than |
the other option would be just have an |
@calvinmetcalf i like that. error listeners are already special anyway |
Done! PTAL |
CI is green |
Yes, those sound better to me, thanks! |
LGTM |
* `listener` {Function} The callback function | ||
|
||
Adds a **one time** `listener` function for the event named `eventName` to the | ||
beginning of the listeners array. This listener is invoked only the next time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should "beginning" be emphasized too for consistency?
I'd like to still take a look at this if possible. May be a day or so. |
myEE.emit('foo'); | ||
|
||
|
||
// Test fail-back if prependListener is undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/fail-back/fallback ?
@mscdex ... updated to address nits |
LGTM. Hopefully once this change has been around for a long while I will finally be able to land my |
Yeah I was going to ping you about that. Would definitely like to get those in soon. |
@jasnell The problem with modules using old |
emitter._events[event].unshift(fn); | ||
else | ||
emitter._events[event] = [fn, emitter._events[event]]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for including the monkeypatch here.
Maybe we can even avoid that, and just move it to readable-stream.
I don't see any case these lines will be executed in core.
cc @nodejs/streams
Anyway, we should land this asap and include it in a release asap, so we can start integrating the change into readable-stream. |
@mcollina ... yep, @Fishrock123 asked for another day or two to review. My plan is to get this landed on Thursday if there are no objections between now and then |
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
Commits squashed and rebased. Planning to get this landed later on today. |
One final CI run: https://ci.nodejs.org/job/node-test-pull-request/2367/ |
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>
Landed in 0e7d57a |
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>
Pull Request check-list
make -j8 test
(UNIX) orvcbuild test nosign
(Windows) pass withthis change (including linting)?
test (or a benchmark) included?
existing APIs, or introduces new ones)?
NOTE: these things are not required to open a PR and can be done
afterwards / while the PR is open.
Affected core subsystem(s)
events
Description of change
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: #1817
/cc @ChALkeR