Skip to content

Instantly share code, notes, and snippets.

@robinpokorny
Last active September 4, 2022 17:27
Show Gist options
  • Save robinpokorny/d743ed9e0bc5214f79076a16c8e44a8f to your computer and use it in GitHub Desktop.
Save robinpokorny/d743ed9e0bc5214f79076a16c8e44a8f to your computer and use it in GitHub Desktop.
📰 Dead simple tweetable JavaScript PubSub pattern module using Set (ES2015)
export default () => {
const subscribers = new Set()
const on = (fn) => { subscribers.add(fn) }
const off = (fn) => subscribers.delete(fn)
const emit = (data) => subscribers.forEach((f) => f(data))
return Object.freeze({ on, off, emit })
}
import pubsub from './pubsub'
// Create a new PubSub
const events = pubsub()
// Register first subscriber
events.on((a) => console.log('one: ' + a))
const two = (a) => console.log('two: ' + a)
// Register second subscriber
events.on(two)
// Subscriber can be registred only once, the following has no effect
events.on(two)
// Dispatch a string
// `two` is called just once
events.emit('foo')
// "one: foo"
// "two: foo"
// Deregister a subscriber
// Returns true if the function has been removed successfully
events.off(two) // true
// Dispatch a string
// `two` is not called
events.emit('bar')
// "one: bar"
@robinpokorny
Copy link
Author

This gist's motivation was to create the simplest full-feature PubSub service.

One might want to check that argument of on is a function to prevent a future type error.

Some inspiration: https://gist.github.com/learncodeacademy/777349747d8382bfb722

@robinpokorny
Copy link
Author

robinpokorny commented Aug 12, 2016

As suggested by @rarous (https://twitter.com/alesroubicek/status/764068304080089089) the on can return an unsub callback and remove the off method:

const on = (fn) => {
  subscribers.add(fn)

  return () => subscribers.delete(fn)
}

@robinpokorny
Copy link
Author

OK, so I liked that idea that much I made it the default. See the original version: https://gist.github.com/robinpokorny/d743ed9e0bc5214f79076a16c8e44a8f/f187d1d45873b1687e0a6d1f754a7aa073736742

@robinpokorny
Copy link
Author

I changed the API so the functions exposed are named sub and pub.
Also, because of the API changes it is not 5-line anymore 😞

@robinpokorny
Copy link
Author

robinpokorny commented Aug 26, 2016

I added a minified/uglified version which fits in a tweet!

Also note that module.exports= is the exact same length as export default (16 characters). So you can easily use it in node.js (>=v0.12) without transpiling.

What I used / found out:

  • Moved functions definitions inside Object.freeze.
  • {s.add(f);return _=>s.delete(f)} to s.add(f).delete.bind(s,f). As Set.prototype.add() returns the Set object we can then access the Set.prototype.delete().
  • _=> is one character less than ()=> and works the same! Of course, any letter would do, too.
  • const is 5 letters, var and let just 3. If it works with const it will work with var and let.

@robinpokorny
Copy link
Author

robinpokorny commented Aug 26, 2016

The following has only 104 bytes, but allows a param during init:

export default (s=new Set)=>Object.freeze({pub:d=>s.forEach(f=>f(d)),sub:f=>s.add(f).delete.bind(s,f)})

I decided not to use this one as it is potentially dangerous.

@robinpokorny
Copy link
Author

Oh, the very minimal version would be 91 characters:

export default (s=new Set)=>({pub:d=>s.forEach(f=>f(d)),sub:f=>s.add(f).delete.bind(s,f)})

That is removing Object.freeze. I would not recommend using that one though…

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