I'm not a huge fan of using CustomEvent for this.. esp. in terms of interoperability (which for these <kb challenges probably doesnt matter)
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()
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.)
if one listener throws it will break the entire channel
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.