Skip to content

Instantly share code, notes, and snippets.

@Kimeiga
Created February 19, 2025 18:04
Show Gist options
  • Save Kimeiga/f3fc28f9131f98701187dbc304750ffe to your computer and use it in GitHub Desktop.
Save Kimeiga/f3fc28f9131f98701187dbc304750ffe to your computer and use it in GitHub Desktop.
pubsub in js

To implement an efficient PubSub (Publish-Subscribe) system in JavaScript, use a Map-based structure to manage subscribers. This approach ensures optimal performance for both subscribing/unsubscribing (O(1) complexity) and publishing (O(n) complexity, unavoidable). Here's the implementation:

Efficient PubSub Implementation

class PubSub {
  constructor() {
    this.events = new Map(); // Event -> Subscribers (Map: ID -> Callback)
  }

  subscribe(event, callback) {
    if (typeof callback !== 'function') {
      throw new Error('Callback must be a function');
    }

    // Get or create the event's subscriber map
    let subscribers = this.events.get(event);
    if (!subscribers) {
      subscribers = new Map();
      this.events.set(event, subscribers);
    }

    // Use a unique Symbol as the subscriber ID
    const id = Symbol();
    subscribers.set(id, callback);

    // Return an unsubscribe function
    return () => subscribers.delete(id);
  }

  publish(event, data) {
    const subscribers = this.events.get(event);
    if (subscribers) {
      // Call all subscribers in insertion order
      subscribers.forEach(callback => {
        try {
          callback(data);
        } catch (error) {
          console.error(`Error in subscriber for ${event}:`, error);
        }
      });
    }
  }
}

Key Features & Efficiency

  1. O(1) Subscription/Unsubscription

    • Uses a nested Map (eventssubscribers) for direct access to subscribers via unique Symbol IDs.
    • Unsubscribing removes the callback instantly without iterating through a list.
  2. Order Preservation

    • Map maintains insertion order, ensuring subscribers are called in the order they subscribed.
  3. Error Handling

    • Catches errors in subscribers to prevent one faulty callback from breaking others.
  4. Memory Efficiency

    • No duplicate entries for the same callback unless explicitly subscribed multiple times.

Usage Example

const pubsub = new PubSub();

// Subscribe
const unsubscribe = pubsub.subscribe('message', (data) => {
  console.log('Received:', data);
});

// Publish
pubsub.publish('message', 'Hello World!'); // Logs "Received: Hello World!"

// Unsubscribe
unsubscribe();

// No output after unsubscribing
pubsub.publish('message', 'Hello again!');

When to Use This Approach

  • Dynamic Subscriptions: Ideal if your app frequently adds/removes subscribers.
  • Order-Sensitive Events: Subscribers are always called in the order they subscribed.
  • High Performance: Optimized for both subscription management and event emission.

Alternatives

  • Array-Based Subscribers: Slightly faster iteration for publishing but slower unsubscription (O(n)).
  • Third-Party Libraries: Use existing solutions like EventEmitter (Node.js) or RxJS for advanced use cases (e.g., streams, filters).

This implementation balances speed, memory, and simplicity, making it the most efficient way to handle PubSub in vanilla JavaScript.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment