Event Bus
Build an in-memory event bus. Subscribers register interest in topics, publishers announce events, and the bus delivers each event to everyone who asked, without the publisher knowing who they are. This is the Observer pattern with a router in the middle.
Implement the class EventBus:
subscribe(pattern, id): subscriberidwants events whose topic matchespattern. A pattern is either an exact topic such as"orders.created", or a prefix wildcard such as"orders.*", which matches every topic that starts with"orders.". A subscriber may hold many patterns, and subscribing twice with the same pattern changes nothing.unsubscribe(pattern, id): remove that one subscription. Returnstrueif it existed andfalseotherwise.publish(topic, message): delivermessageto every subscriber that has at least one pattern matchingtopic. Returns how many subscribers received it. A subscriber whose patterns match twice (for example"orders.*"and"orders.created") still receives the message once.inbox(id): everythingidhas received so far, as[topic, message]pairs, oldest first. An unknown subscriber has an empty inbox.
Messages published before a subscription existed are not delivered to it later.
Notes
"orders.*"does not match"orders"itself, and"*"on its own is not a pattern.- Wildcards apply only at the end. A topic never contains
*. - Delivery order inside one
publishdoes not matter, only the order within each inbox.
Examples
Example 1
Input: ["EventBus","subscribe","subscribe","publish","publish","inbox","inbox"]
[[],["orders.created","a"],["orders.created","b"],["orders.created","new order"],["orders.shipped","shipped"],["a"],["b"]]
Output: [null,null,null,2,0,[["orders.created","new order"]],[["orders.created","new order"]]]Both subscribers asked for orders.created, so both receive the first event and the count is 2. The second event has no subscribers, so nothing is delivered.
Example 2
Input: ["EventBus","subscribe","publish","publish","publish","inbox"]
[[],["orders.*","a"],["orders.created","x"],["orders.shipped","y"],["orders","z"],["a"]]
Output: [null,null,1,1,0,[["orders.created","x"],["orders.shipped","y"]]]orders.* matches every topic that starts with orders. but not the bare topic orders, so the third publish reaches nobody.
Example 3
Input: ["EventBus","subscribe","subscribe","publish","inbox"]
[[],["orders.*","a"],["orders.created","a"],["orders.created","once"],["a"]]
Output: [null,null,null,1,[["orders.created","once"]]]Two patterns of the same subscriber match, yet the event is delivered once and counted once.
Each test creates the class with the first operation, then calls the methods in order. Output lists what each call returns; null marks the constructor and methods that return nothing, and their return values aren't checked.
class EventBus {
constructor() {
}
/** Subscribe `id` to a topic pattern: an exact topic ("orders.created") or a prefix wildcard ("orders.*"). */
subscribe(pattern, id) {
}
/** Remove one subscription. @return {boolean} true if it existed */
unsubscribe(pattern, id) {
}
/** Deliver the message to every subscriber with a matching pattern. @return {number} how many subscribers received it */
publish(topic, message) {
}
/** @return {Array<[string, string]>} everything `id` has received, as [topic, message], oldest first */
inbox(id) {
}
}
Ctrl/⌘ + ' runs the examples, Ctrl/⌘ + Enter submits. Use console.log freely: output shows under Console. In the editor, press Esc then Tab to move focus out.