Skip to content

Conversation

@vanruesc
Copy link
Contributor

Description

This PR aims to enhance EventDispatcher by replacing the current array-based implementation with a map-based one. This removes the need for copying the listener array on each dispatchEvent call since Map iteration supports adding and removing elements during iteration. See https://262.ecma-international.org/#sec-map.prototype.foreach for more details.

In addition to listener functions, this PR also adds support for listener objects that implement a handleEvent(event) method to be more in line with EventTarget.

These changes are suggestions - the current implementation works just fine. I've tested webgl_shadowmap_performance which dispatches a lot of events and saw no obvious difference between the old and the new implementation in terms of performance and allocations.

@github-actions
Copy link

github-actions bot commented Jan 11, 2026

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 355.41
84.51
355.41
84.51
+0 B
+0 B
WebGPU 621.21
172.51
621.21
172.51
+0 B
+0 B
WebGPU Nodes 619.82
172.27
619.82
172.27
+0 B
+0 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 487.36
119.29
487.53
119.3
+169 B
+13 B
WebGPU 691.86
187.79
692.03
187.82
+169 B
+23 B
WebGPU Nodes 641.66
174.96
641.83
174.98
+169 B
+25 B

@Mugen87
Copy link
Collaborator

Mugen87 commented Jan 12, 2026

If you say there is no difference performance-wise, then I don't understand the motivation of this refactoring, tbh. If the current implementation works find and has no obvious flaws, I would not risk any sort of regression or unexpected behavior.

@vanruesc
Copy link
Contributor Author

Basically this line motivated me to make this PR. Even though modern JavaScript engines seem to optimize this away, it's still not optimal for code that potentially runs very often. Switching to modern language features seemed like a safer bet in the long run ...and I was hoping for handleEvent support 😅

But I see your point; it's possible that users extend EventDispatcher and treat _listeners as public API, even though it shouldn't be. Feel free to close this.

@Mugen87
Copy link
Collaborator

Mugen87 commented Jan 12, 2026

Um, wouldn't reversing the loop order make the copy obsolete as well?

for ( let i = listenerArray.length - 1; i >= 0; i -- ) {

    listenerArray[ i ].call( this, event );

}

@vanruesc
Copy link
Contributor Author

Good idea. I wasn't sure if this works so I tested it, but unfortunately it fails when multiple listeners are removed during dispatchEvent: https://jsfiddle.net/qc7avky9/3/

This would also obviously change the listener execution order which some users may (unknowingly) rely on.

There are probably more ways to get rid of the array copy in dispatchEvent, maybe with some two-layer approach: one working array and one iteration array with a dirty flag, but I like the simplicity of the map approach 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants