logoalt Hacker News

arnorhslast Tuesday at 11:53 AM4 repliesview on HN

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()

Replies

Joerilast Tuesday at 12:56 PM

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.

show 2 replies
chrismorganlast Wednesday at 2:26 AM

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.)

show 1 reply
nsonhalast Wednesday at 3:36 AM

if one listener throws it will break the entire channel