perf: EventEmitter methods on one shared prototype with node's _events store (Refs #10508) - #11809
Merged
Merged
Conversation
added 2 commits
October 3, 2026 15:30
A class extending EventEmitter (and util.inherits + EventEmitter.call, Object.create(EventEmitter.prototype), setPrototypeOf mixins) no longer gets the 15 emitter closures stamped onto every instance. super() and EventEmitter.call(this, opts) run node's EventEmitter.init: own _events (null prototype), _eventsCount and _maxListeners, plus captureRejections. EventEmitter.prototype carries node's defaults, the methods in lib/events.js order and the addListener/on and off/removeListener aliases, as IMPLICIT_THIS closures shared by every emitter. The listener store is node's _events (one function or an array per type, once wrappers with .listener, newListener/removeListener through this.emit), shared with the node:stream objects. A subclass override sits on its own prototype, and super.m() walks the receiver's chain to the native body, which keeps the #6316 behavior without the per-instance stash. A captured rejection reaches [Symbol.for('nodejs.rejection')] or emit('error'); the capture flag is read only for a listener that returned a promise. t508: new 151.3k -> 77.1k instructions (RSS 108 -> 47 MB), emit 51.1k -> 34.2k, method call unchanged; commander parse_argv -22.1%; tsc and Zod flat. Refs #10508
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (17)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #10508. EventEmitter subclass instances no longer own their methods: they inherit from one shared
EventEmitter.prototype, and listener state uses node's_eventsstore.Why it was per-instance (#6316).
super()stamped 15 closures onto every instance, which shadowed a subclass'semit()override, so #6316 hid overridden base methods under a private key. With a real prototype chain the override wins naturally.super.m()walks the chain to the native method and checks it is the native one.What changed
super(opts)andEventEmitter.call(this, opts)behave like node'sEventEmitter.init: own_events,_eventsCount,_maxListeners, pluscaptureRejections.EventEmitter.prototypegets node's defaults and its methods in node's order, withaddListener === onandoff === removeListener.oncewrappers carry.listener.captureRejectionsnow works for subclasses. Rejections go toSymbol.for('nodejs.rejection')or toemit('error'); a non-boolean option throwsERR_INVALID_ARG_TYPE.Instructions (n=5)
newof an EventEmitter subclassnewloopTests
hasOwnProperty('on'), overrides withsuper, seven heritage forms,util.inherits,EventEmitter.call(this),Object.create, mixins, the listener API, errors,captureRejections, and a 3000-emitter loop under moving GC.Not done
new EventEmitter()is still a handle, so itsObject.keysis[]where node prints the three state keys; that needs a separate port.q.emitlookup costs ~24k of emit_ee (method-call follow-up).EventEmitterAsyncResourcestill install their methods per instance.