Build a JavaScript Publish-Subscribe System
This challenge asks you to implement a fundamental pattern in modern software architecture: the Publish-Subscribe (Pub-Sub) system. A Pub-Sub system allows different parts of your application to communicate without direct knowledge of each other, promoting loose coupling and scalability. You will build a core engine that enables publishers to send messages and subscribers to receive them based on specific topics.
Problem Description
Your task is to create a JavaScript class, PubSub, that acts as a central hub for message dissemination. This system should support the following core functionalities:
- Subscribing: Allow multiple subscribers to register their interest in specific "topics". A topic is simply a string identifier (e.g., "userLoggedIn", "newOrder").
- Publishing: Allow publishers to send messages to a specific topic. When a message is published, all subscribers currently listening to that topic should be notified and receive the message.
- Unsubscribing: Allow subscribers to unregister their interest in a topic, so they no longer receive messages for it.
Key Requirements:
- The
PubSubclass should have methods likesubscribe,publish, andunsubscribe. - Subscribers should be functions (callbacks) that are executed when a message is published to their subscribed topic.
- Messages can be any JavaScript data type.
- A subscriber should be able to subscribe to multiple topics.
- A topic can have multiple subscribers.
- Unsubscribing should remove a specific subscriber function from a specific topic. If no specific function is provided for unsubscribe, it should remove all subscribers for that topic.
Expected Behavior:
- When
publishis called for a topic, all registered subscriber functions for that topic should be executed, passing the message data as an argument. - If a topic has no subscribers,
publishshould do nothing. - If
unsubscribeis called with a specific subscriber function, only that function should be removed for that topic. - If
unsubscribeis called without a subscriber function, all subscribers for that topic should be removed.
Edge Cases to Consider:
- Subscribing with a non-function callback.
- Publishing to a topic that no one has subscribed to.
- Unsubscribing a subscriber that was never subscribed.
- Unsubscribing a topic that has no subscribers.
- Concurrent subscriptions and publications (though for this challenge, sequential execution is generally assumed unless specified).
Examples
Example 1:
const pubsub = new PubSub();
const userLoginHandler = (userData) => {
console.log(`User logged in: ${userData.name}`);
};
const notificationHandler = (message) => {
console.log(`Notification received: ${message}`);
};
pubsub.subscribe("userLoggedIn", userLoginHandler);
pubsub.subscribe("notification", notificationHandler);
pubsub.publish("userLoggedIn", { id: 1, name: "Alice" });
// Expected Output:
// User logged in: Alice
pubsub.publish("notification", "Welcome back!");
// Expected Output:
// Notification received: Welcome back!
Example 2:
const pubsub = new PubSub();
const handlerA = () => console.log("Handler A called");
const handlerB = () => console.log("Handler B called");
pubsub.subscribe("event1", handlerA);
pubsub.subscribe("event1", handlerB);
pubsub.subscribe("event2", handlerA);
pubsub.publish("event1", "message for event1");
// Expected Output:
// Handler A called
// Handler B called
pubsub.publish("event2", "message for event2");
// Expected Output:
// Handler A called
pubsub.unsubscribe("event1", handlerA);
pubsub.publish("event1", "another message for event1");
// Expected Output:
// Handler B called
Example 3: Unsubscribing all subscribers for a topic
const pubsub = new PubSub();
const handlerX = () => console.log("Handler X called");
const handlerY = () => console.log("Handler Y called");
pubsub.subscribe("topicZ", handlerX);
pubsub.subscribe("topicZ", handlerY);
pubsub.publish("topicZ", "initial message");
// Expected Output:
// Handler X called
// Handler Y called
pubsub.unsubscribe("topicZ"); // Unsubscribe all for topicZ
pubsub.publish("topicZ", "subsequent message");
// Expected Output: (No output)
Constraints
- The
PubSubclass should be implemented using standard JavaScript features (ES6+ is acceptable). - No external libraries or frameworks are allowed.
- The implementation should be reasonably efficient, especially for handling a large number of topics and subscribers. Consider the time complexity of your
subscribe,publish, andunsubscribeoperations. - Topics are case-sensitive strings.
Notes
- Think about how you will store the subscriptions. A common approach is to use an object where keys are topic names and values are arrays of subscriber functions.
- When publishing, you'll need to iterate through the list of subscribers for a given topic and execute each one.
- Be mindful of how you handle unsubscribing to ensure that the correct subscriber is removed and that the system remains stable.
- Consider how to handle potential errors, such as attempting to unsubscribe a non-existent subscriber. For this challenge, you can choose to either throw an error or gracefully do nothing.