The API feels wrong. The object that was passed to pub() is the object that should be received by the callback passed to sub().
The use of EventTarget/CustomEvent is an implementation detail; it should not be part of the API.
As a result, every callback implementation is larger because it must explicitly unwrap the CustomEvent object.
Essentially, the author made the library smaller by pushing necessary code to unwrap the CustomEvent object to the callsites. That's the opposite of what good libraries do!
The mentioned nano-pubsub gets this right, and it even gets the types correct (which the posted code doesn't even try).
The point of this exercise, to my mind, is to show the utter simplicity of pub-sub. Such code belongs to the API documentation, like the code snippets on MDN.
Proper code would have expressive parameter names, good doc comments, types (TS FTW) and the niceties like unpacking you mention. One of them would be named topics mapped to EventTargets, so that publishers and subscribers won't need to have visibility into this implementation detail.
If listeners of this implementation aren’t unsubscribed they can’t be garbage collected, and in a real world codebase that means memory leaks are inevitable. EventDispatcher has weak refs to its listeners, so it doesn’t have this problem.
The listeners can be garbage-collected if the `greetings` publisher object and any unsubscribe callbacks are garbage-collectable. This is consistent with normal Javascript EventTargets which don't use weak refs.
If only weak refs were kept to listeners, then any listeners you don't plan to unsubscribe and don't keep that callback around will effectively auto-unsubscribe themselves. If this was done and you called `greetings.sub((name) => console.log("hi there", name));` to greet every published value, then published values will stop being greeted whenever a garbage collection happens.
Using the event dispatch mechanism is flat-out bigger, anyway. Here’s the interface of the original script (that is, global pub/sub functions taking a name), except that the receiver site no longer needs to look at the .detail property so it’s better:
let t={};
sub=(e,c)=>((e=t[e]??=new Set).add(c),()=>e.delete(c));
pub=(n,d)=>t[n]?.forEach(f=>f(d))
The original was 149 bytes; this is 97.
(The nullish coalescing assignment operator ??= has been supported across the board for 4½ years. Avoiding it will cost six more bytes.)
This isn't the same though. With EventTarget, if one of the callback throws, the later callbacks would still get called. With yours the later callbacks don't get called.
Incredibly useful, especially with React, where the Context API, state lifting, and prop drilling often feel clunky. That said, it can lead to messy code if not carefully managed.
Bingo! Having tons of `CustomEvents` with arbitrary handlers gets unwieldy. One way we "solved" this is by only allowing custom events in a `events.ts` file and document them pretty extensively.
This has been a popular technique at times, but it tends to increase compressed sizes: gzip and similar are better at common string deduplication, having lower overhead. Such shenanigans are also bad for performance, especially in hot paths due to making it harder for the browser to optimise it.
It seems like it yeah. I did something that looks similar at a surface-level (https://github.com/victorb/LightYearJS/blob/master/test/acce...) around 11 years ago, but apparently called it an "Event Dispatcher", something that might fit the submission project better.
The use of EventTarget/CustomEvent is an implementation detail; it should not be part of the API.
As a result, every callback implementation is larger because it must explicitly unwrap the CustomEvent object.
Essentially, the author made the library smaller by pushing necessary code to unwrap the CustomEvent object to the callsites. That's the opposite of what good libraries do!
The mentioned nano-pubsub gets this right, and it even gets the types correct (which the posted code doesn't even try).
Proper code would have expressive parameter names, good doc comments, types (TS FTW) and the niceties like unpacking you mention. One of them would be named topics mapped to EventTargets, so that publishers and subscribers won't need to have visibility into this implementation detail.
The usage, to me, feels appropriate for JS.
I agree that event.detail should be returned instead of the whole event. Can definitely save some space at the callsites there!
personally, i'll just roll with something like this which also is typed etc:
If only weak refs were kept to listeners, then any listeners you don't plan to unsubscribe and don't keep that callback around will effectively auto-unsubscribe themselves. If this was done and you called `greetings.sub((name) => console.log("hi there", name));` to greet every published value, then published values will stop being greeted whenever a garbage collection happens.
(The nullish coalescing assignment operator ??= has been supported across the board for 4½ years. Avoiding it will cost six more bytes.)
Well then, a few alternatives to replace f=>f(d), each with slightly different semantics:
• async f=>f(d) (+6, 103 bytes).
• f=>{try{f(d)}catch{}} (+14, 111 bytes).
• f=>setTimeout(()=>f(d)) (+16 bytes, 113 bytes).
• f=>queueMicrotask(()=>f(d)) (+20 bytes, 117 bytes).
https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent...
here's my implementation from a while back with `setTimeout` like semantics; used it to avoid prop-drilling in an internal dashboard (sue me)
https://gist.github.com/thewisenerd/768db2a0046ca716e28ff14b...
Multiple independent listeners should be able to attach a callback that fires when “hello” is called.
https://nuejs.org/