Hone logo
Hone
Problems

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:

  1. Subscribing: Allow multiple subscribers to register their interest in specific "topics". A topic is simply a string identifier (e.g., "userLoggedIn", "newOrder").
  2. 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.
  3. Unsubscribing: Allow subscribers to unregister their interest in a topic, so they no longer receive messages for it.

Key Requirements:

  • The PubSub class should have methods like subscribe, publish, and unsubscribe.
  • 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 publish is 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, publish should do nothing.
  • If unsubscribe is called with a specific subscriber function, only that function should be removed for that topic.
  • If unsubscribe is 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 PubSub class 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, and unsubscribe operations.
  • 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.
Loading editor...
javascript