Design Patterns
Observer Pattern (PubSub)
Master the Observer and Publish-Subscribe patterns in JavaScript. Learn to implement subject-observer loops, emit events, and manage event channels.
1. Introduction
The Observer Pattern is a design pattern where an object (the Subject) maintains a list of dependents (the Observers) and notifies them automatically of any state changes. A closely related pattern is the Publish-Subscribe (PubSub) pattern, which uses an intermediate message broker channel to decouple publishers and subscribers.
2. Why It Matters
Direct coupling between modules makes applications rigid. If a user action in component A needs to trigger updates in components B, C, and D, writing direct method calls inside component A couples it to those specific components. The Observer pattern solves this by enabling event-driven, decoupled communication.
3. Real-World Analogy
Think of News Subscription Channels:
- Direct Coupling (Calling friends): You discover a news update. You manually call every friend (observers) one by one to tell them. If you add a new friend, you must modify your contact list and call them too.
- Observer Pattern (Newsletter List): You set up a newsletter sign-up sheet. Anyone who wants updates writes their email on the list (registers observer). When you publish an update, you send it to everyone on the list automatically. You don't need to know who is on the list to write the update.
- PubSub Pattern (Radio Tower): Instead of maintaining the email list yourself, you broadcast your update over a radio frequency (Event Channel). Anyone who tunes their radio receiver to that frequency (subscribes) hears the update. The broadcaster and listeners are completely anonymous to each other.
4. Implementing the Observer Pattern
Here is an implementation of a Subject class that manages and notifies Observer instances:
5. Implementing PubSub
The Publish-Subscribe (PubSub) pattern uses a central message broker channel. Publishers and subscribers do not know about each other:
6. Practical Example
This script demonstrates using PubSub to sync updates between a shopping cart and a navigation bar display in a web application:
7. Common Mistakes
- Failing to unsubscribe observers when components unmount: Forgetting to unsubscribe event callbacks when UI components unmount creates reference leaks. The event channel retains references to the component callbacks, preventing the garbage collector from freeing them and causing memory leaks (known as Lapsed Listener leaks).
8. Quick Quiz
Q1: What is the key architectural difference between the Observer pattern and the PubSub pattern?
A) Observer is asynchronous while PubSub is synchronous
B) Observer couples the Subject and Observers directly, while PubSub uses a central message broker channel to decouple them completely
Answer: B — The Observer pattern requires the Subject to maintain references to its observers. PubSub uses a central broker, making publishers and subscribers anonymous to each other.
9. Scenario-Based Challenge
The Multi-Client Chat Hub Emiter:
A chat application needs to broadcast messages to multiple user feeds. When a message is received: onMessage(payload), publish it to the message-stream channel. If a client disconnects, ensure they are unsubscribed to prevent resource leaks. Write this routing interface.
10. Debugging Exercise
Explain why this event listener causes a memory leak, and how to fix it:
const pub = new PubSub();class TabComponent { constructor() { // Bug: registering an anonymous arrow function callback! pub.subscribe('theme-change', (theme) => { this.updateTheme(theme); }); }
updateTheme(theme) { /* ... */ } destroy() { // How do we unsubscribe since the callback was anonymous? } }
View Solution
Diagnosis: The class registers an anonymous callback function, which makes it impossible to reference and unsubscribe during component destruction. The PubSub instance retains a reference to the callback, preventing the class instance from being garbage collected and causing a memory leak.
Fix: Store the unsubscribe function returned by the subscribe call, and execute it during destruction:
class TabComponent { #unsub;constructor() { this.#unsub = pub.subscribe('theme-change', (theme) => { this.updateTheme(theme); }); }
updateTheme(theme) { /* ... */ }
destroy() { this.#unsub(); // Safely unsubscribe and release reference! } }
11. Interview Questions
🟢 Q1: Explain the Lapsed Listener problem and describe how to prevent it in JavaScript applications.
Answer: The Lapsed Listener problem is a memory leak that occurs when an observer subscribes to a subject but is never unsubscribed when it is no longer needed.
• Why it happens: The subject retains a reference to the observer's callback, preventing the garbage collector from freeing the observer instance.
• Prevention: Always unsubscribe observers when they are no longer needed (such as in React's useEffect cleanup function or a class destroy() method) or use weak references (like WeakRef) to allow the garbage collector to free observers automatically.
12. Production Considerations
- • Cleanup Hooks: When using PubSub inside React components, always return the unsubscribe function from the
useEffectcleanup hook to release event callbacks and prevent memory leaks.