JavaScript Observer Pattern [In-Depth Tutorial]


Written by - Olorunfemi Akinlua
Reviewed by - Deepak Prasad

What is the JavaScript Observer Pattern?

The Observer Pattern is a behavioral design pattern that allows an object (the subject) to notify other objects (the observers) when its state changes. In other words, when the subject undergoes a change, it sends a notification to the observers, informing them of the change. This pattern is used to decouple objects, making them independent of each other, and promoting code reusability.

 

Implementing Observer Patterns in JavaScript

In JavaScript, the Observer Pattern can be implemented in several ways. Here we will discuss two examples of how to implement the Observer Pattern in JavaScript.

To illustrate how to implement observer patterns, we will implement the Observer Pattern using the built-in EventTarget interface. The EventTarget interface provides a way for objects to be notified of events that occur on a target object.

class Subject extends EventTarget {
    constructor() {
        super();
        this.value = null;
    }

    setValue(newValue) {
        this.value = newValue;
        this.dispatchEvent(new Event("change"));
    }
}

class Observer {
    constructor(subject) {
        this.subject = subject;
        this.subject.addEventListener("change", this.update.bind(this));
    }

    update() {
        console.log(`Value has been updated: ${this.subject.value}`);
    }
}

const subject = new Subject();
const observer = new Observer(subject);

subject.setValue("new value");

Output

Value has been updated: new value

In this example, we have a Subject class that extends the EventTarget interface. The Subject class has a method called setValue that sets the value of the subject and dispatches an event called change. The Observer class listens for the 'change' event on the subject and logs a message to the console when the event is fired. Finally, we create a subject and observer instance, and call the setValue method on the subject to trigger the 'change' event.

In another example, we will implement the Observer Pattern using custom events and callbacks.

class Subject {
  constructor() {
    this.value = null;
    this.observers = [];
  }

  setValue(newValue) {
    this.value = newValue;
    this.notifyObservers();
  }

  addObserver(callback) {
    this.observers.push(callback);
  }

  removeObserver(callback) {
    this.observers = this.observers.filter(observer => observer !== callback);
  }

  notifyObservers() {
    this.observers.forEach(observer => observer(this.value));
  }
}

const subject = new Subject();

const observer1 = value => {
  console.log(`Observer 1 has been notified: ${value}`);
};

const observer2 = value => {
  console.log(`Observer 2 has been notified: ${value}`);
};

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.setValue('new value');

subject.removeObserver(observer2);

subject.setValue('another value');

Output

Observer 1 has been notified: new value
Observer 2 has been notified: new value
Observer 1 has been notified: another value

In this example, we have a Subject class that has a method called setValue that sets the value of the subject and calls the notifyObservers method. The Subject class also has methods for adding and removing observers, and a method for notifying observers. The observers are simply callback functions that are added to an array of observers in the Subject class. When the notifyObservers method is called, it loops through the array of observers and calls each observer with the current value of the subject. Finally, we create a subject instance and two observer instances that are added to the subject as observers using the addObserver method. We call the setValue method on the subject to trigger the notifyObservers method and log a message to the console for each observer. Finally, we remove the second observer using the removeObserver method and call the setValue method again to trigger the notifyObservers method and log a message to the console for the remaining observer.

 

How does the Observer pattern differ from the Pub/Sub pattern

The Observer pattern and the Pub/Sub (publish-subscribe) pattern are two common patterns used in event-driven programming in JavaScript. Although they have some similarities, there are a few key differences between the two patterns.

  • Communication direction: In the Observer pattern, there is a direct communication channel between the subject (or publisher) and its observers (or subscribers). The subject notifies its observers directly, and the observers have no knowledge of other observers. In the Pub/Sub pattern, there is a third-party mediator (called a "broker" or "hub") that acts as an intermediary between publishers and subscribers. Publishers publish messages to the broker, which then forwards those messages to all interested subscribers.
  • Relationship between subject and observers: In the Observer pattern, the subject maintains a list of its observers and notifies them when its state changes. In the Pub/Sub pattern, there is no direct relationship between publishers and subscribers. Publishers publish messages to the broker, which then forwards those messages to all subscribers that are interested in that type of message.
  • Coupling: In the Observer pattern, the subject and its observers are tightly coupled, because the subject has knowledge of its observers. In the Pub/Sub pattern, publishers and subscribers are loosely coupled, because they communicate through a third-party mediator.
  • Flexibility: The Pub/Sub pattern is generally considered to be more flexible than the Observer pattern, because it allows for a greater degree of decoupling between publishers and subscribers. Publishers don't need to know anything about their subscribers, and vice versa. Additionally, the broker in a Pub/Sub system can be used to implement more advanced features, such as message filtering, topic-based subscriptions, and message queuing.

Overall, the choice between the Observer pattern and the Pub/Sub pattern will depend on the specific needs of the application. If direct communication between the subject and its observers is sufficient, and if tight coupling between the two is not a concern, then the Observer pattern may be a good choice. If more flexibility and decoupling are desired, or if a third-party mediator is required for other reasons, then the Pub/Sub pattern may be a better fit.

 

Handling errors or exceptions when using the Observer pattern

When using the Observer pattern in JavaScript, there are a few strategies you can use to handle errors or exceptions that might occur:

  • Handle errors in the observer callbacks: When an observer receives a notification from the subject, it may perform some action that could potentially throw an error. To handle this, you can wrap the observer callback in a try-catch block to catch any exceptions that occur. Alternatively, you can provide error-handling functions as additional arguments to the observer callbacks, which the callbacks can then call if an error occurs.
  • Handle errors in the subject: When a subject is notifying its observers, it may encounter an error that prevents it from notifying all of them. To handle this, you can catch any errors that occur in the subject's notification method and handle them appropriately. For example, you could log the error to the console, notify a subset of observers that were successfully notified, or implement some kind of retry mechanism.
  • Provide default values: When an observer receives a notification from the subject, it may assume that certain data is present or in a certain format. If this assumption is incorrect, it could result in an error. To avoid this, you can provide default values for any data that is expected to be present in the notification. Alternatively, you can use a schema validation library to validate the data before processing it.
  • Use a middleware or interceptor: A middleware or interceptor can be used to intercept and modify the notifications sent by the subject before they are received by the observers. This can be useful for adding error-handling or data-validation logic to the notification process.

 

How to avoid Memory Leaks with Observer Pattern

Memory leaks can occur when using the Observer pattern if observers are not properly removed when they are no longer needed. Here are a few strategies you can use to avoid memory leaks:

  • Remove observers when they are no longer needed: When an observer is added to a subject, it should be removed when it is no longer needed. This can be done by implementing a "removeObserver" method on the subject, or by providing a way for the observer to remove itself (such as an "unsubscribe" method).
  • Use weak references: Weak references are references to objects that do not prevent those objects from being garbage collected. In JavaScript, weak references can be implemented using the WeakMap or WeakSet data structures. When using weak references to store observers, they will automatically be removed when they are no longer referenced elsewhere in the code.
  • Implement reference counting: Reference counting is a technique used to keep track of how many references there are to an object. When the reference count reaches zero, the object can be safely garbage collected. When implementing the Observer pattern, you can use reference counting to keep track of how many subjects are referencing each observer. When the reference count for an observer reaches zero, it can be safely removed.
  • Use a library: There are many libraries available that implement the Observer pattern and handle memory management for you. For example, the RxJS library provides a powerful implementation of the Observer pattern that includes features such as automatic memory management and error handling.

 

Advantages and Disadvantages of the JavaScript Observer Pattern

The JavaScript Observer Pattern provides a flexible and powerful way to establish one-to-many relationships between objects and improve code organization and performance. However, it also has potential drawbacks to consider, such as increased complexity, performance overhead, and the potential for memory leaks. By understanding the advantages and disadvantages of the pattern, developers can make informed decisions about when and how to use it effectively.

 

Summary

In this article, we have discussed the JavaScript Observer Pattern, a behavioral design pattern that allows an object to notify other objects when its state changes. We have also provided examples of how to implement the Observer Pattern in JavaScript using the built-in EventTarget interface and custom events and callbacks. The Observer Pattern is a powerful pattern that promotes code reusability and maintainability, and it is widely used in JavaScript applications. By understanding the Observer Pattern and how to implement it in JavaScript, developers can create better, more robust applications that are easier to maintain and extend.

 

References

Observer pattern - Wikipedia
The Observer Pattern - Learning JavaScript Design Patterns [Book] (oreilly.com)

 

Views: 0

Olorunfemi Akinlua

He is boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs user-friendly interfaces to enhance digital experiences across various domains. You can connect with him on LinkedIn.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment