Last active
May 16, 2026 03:48
-
-
Save night-fury-rider/f10093cdfffbbac12665da76e3692479 to your computer and use it in GitHub Desktop.
Concurrency Pattern - Pub/Sub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * PubSub (Publish-Subscribe) Pattern Implementation | |
| * | |
| * A lightweight event bus that decouples producers (publishers) from | |
| * consumers (subscribers). Publishers emit named events with data; | |
| * subscribers register callbacks that fire when those events are published. | |
| */ | |
| class PubSub { | |
| #events = new Map(); | |
| subscribe(eventName, callback) { | |
| // Only support function as callback | |
| if (typeof callback !== "function") { | |
| throw new TypeError(`Subscriber for ${eventName} must be a function`); | |
| } | |
| if (!this.#events.has(eventName)) { | |
| this.#events.set(eventName, new Set()); | |
| } | |
| this.#events.get(eventName).add(callback); // Set ignores duplicate refs | |
| return () => { | |
| this.unsubscribe(eventName, callback); | |
| }; | |
| } | |
| unsubscribe(eventName, callback) { | |
| this.#events.get(eventName)?.delete(callback); | |
| } | |
| // Used async/await to support async publishing of event | |
| async publish(eventName, data) { | |
| const subscribers = this.#events.get(eventName); | |
| if (!subscribers?.size) { | |
| return; | |
| } | |
| // Snapshot before iterating so mid-publish unsubscribes don't skew results | |
| const subscribersSnapshot = [...subscribers]; | |
| const result = await Promise.allSettled( | |
| subscribersSnapshot.map((callback) => callback(data)), | |
| ); | |
| for (let i = 0; i < result.length; i++) { | |
| if (result[i].status === "rejected") { | |
| console.error( | |
| `Subscriber ${i} threw an error for ${eventName}`, | |
| results[i].reason, | |
| ); | |
| } | |
| } | |
| } | |
| clearEvent(eventName) { | |
| this.#events.delete(eventName); | |
| } | |
| clear() { | |
| this.#events.clear(); | |
| } | |
| } | |
| const cart = { | |
| items: [], | |
| total: 0, | |
| }; | |
| const product1 = { | |
| name: "Earphone", | |
| price: 2500, | |
| }; | |
| const product2 = { | |
| name: "Keyboard", | |
| price: 1100, | |
| }; | |
| const lockProductPrice = (product) => { | |
| console.log(`Price of ${product.name} is locked at ₹ ${product.price}`); | |
| }; | |
| const updateCart = (product) => { | |
| const existingItem = cart.items.find((obj) => obj.name === product.name); | |
| if (existingItem) { | |
| existingItem.qty += 1; | |
| } else { | |
| cart.items.push({ ...product, qty: 1 }); | |
| } | |
| cart.total = cart.items.reduce((acc, item) => { | |
| return acc + item.price * item.qty; | |
| }, 0); | |
| console.log(`Cart Total: ₹ ${cart.total}`); | |
| }; | |
| const onNotification = (notification) => { | |
| console.log("New notification:", notification.message); | |
| }; | |
| // Subscriber | |
| const pubSub = new PubSub(); | |
| pubSub.subscribe("cart:add", lockProductPrice); | |
| pubSub.subscribe("cart:add", updateCart); | |
| const unsubscribeNotification = pubSub.subscribe( | |
| "notification:new", | |
| onNotification, | |
| ); | |
| // Publisher | |
| await pubSub.publish("cart:add", product1); | |
| await pubSub.publish("notification:new", { message: "*** Item Added to Cart" }); | |
| await pubSub.publish("cart:add", product2); | |
| await pubSub.publish("notification:new", { message: "*** Item Added to Cart" }); | |
| await pubSub.publish("cart:add", product1); // triggers qty increment for Earphone | |
| await pubSub.publish("notification:new", { message: "*** Item Added to Cart" }); | |
| // Unsubscribe from Notification | |
| unsubscribeNotification(); | |
| // Does not publish the event since we unsubscribed from notification | |
| await pubSub.publish("notification:new", { message: "Use Supercoins" }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment