I have this pubsub implementation and I was just curious about how would you reduce it any further.
I'm talking about the implementation, not the TypeScript part.
type Observer<T> = (payload: T) => void;
export interface Publer<T> {
subscribe<K extends keyof T>(
channel: K,
observer: Observer<T[K]>,
): () => void;
unsubscribe<K extends keyof T>(channel?: K): void;
publish<K extends keyof T>(
channel: K,
...a: T[K] extends undefined ? [] : [T[K]]
): void;
}
export const publer = <T>(): Publer<T> => {
const eventMap = new Map<keyof T, Set<Observer<any>>>();
return {
subscribe: (channel, observer) =>
(eventMap.get(channel)?.add(observer) ||
eventMap.set(channel, new Set([observer]))) &&
(() => eventMap.get(channel)?.delete(observer)),
unsubscribe: (channel) =>
channel ? eventMap.get(channel)?.clear() : eventMap.clear(),
publish: (channel, ...[payload]) =>
eventMap.get(channel)?.forEach((observer) => observer(payload)),
} as const;
};
This is the smallest way I could come up with.
interface Events {
login: { token: string }
}
const pubsub = publer<Events>();
const unsubscribe = pubsub.subscribe('login', ({ token }) => {
// Payload is inferred based on the event name
})
// Payload is required based on event name
pubsub.publish('login', { token: "ABC" });
I would love to see what other ways to make it even smaller you guys think of. Cheers!
Top comments (2)
subscriber
returnunsubscribe
function can simplify the unsubscribe logic since all the needed pointers can be accessed within the closure (which you do already). And I kinda like that pubsub will only return pub and sub:-)channel
is always a string it can be a bit smaller if one uses pure objects instead of map to store channel observers.[pub,sub]
instead of object with named propertiesBut, honestly, I'm not sure why it needs to be smaller, apart from the obvious challenge:-)
Looks good the way it is
Nice! I took on your suggestion regarding the tuple and now it weights 150 bytes gzipped having removed the "global" unsubscribe function, leaving only the unsubscribe from subscribe.
I didn't swap the Map with the plain object because I'd need to get rid of the subscribe one-liner.
Anyways, thanks for the feedback. The reason I asked here is because publer is a real package on NPM and I'm kinda obsessed about releasing the smallest possible code without loosing functionality.