personally, i'll just roll with something like this which also is typed etc:
export function createPubSub<T extends readonly any[]>() {
const l = new Set<(...args: T) => void>()
return {
pub: (...args: T) => l.forEach((f) => f(...args)),
sub: (f: (...args: T) => void) => l.add(f) && (() => l.delete(f)),
}
}
// usage:
const greetings = createPubSub<[string]>()
const unsubscribe = greetings.sub((name) => {
console.log('hi there', name)
})
greetings.pub('Dudeman')
unsubscribe()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.
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.)
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!
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...
sub => ref = 0
sub => ref = 1
unsub(0)
sub => ref = 1 (two subs with same ref!) // Lib code>>
s={};call=(n)=>{s[n]()}
// <<
s.hello=()=>console.log('hello');
call('hello');
delete s.hello;Multiple independent listeners should be able to attach a callback that fires when “hello” is called.
export default new Map