* Observer Pattern is widely used in Javascript. This paper will first clarify the basic implementation of Observer Pattern in Javascript. Then focus on the current popular tool library – RxJS – to further study the implementation principle of observer mode.
The pattern structure of the observer pattern
The observer pattern defines a one-to-many dependency between objects so that when the state of each dependent object changes, its dependent objects are notified and automatically updated.
The dependent object is usually called Subject. In order to be consistent with the concept in RxJS, it is called Observable, and its related dependent object is called Observer. In other words, the observed object will actively “push” the message to the Observer. Instead of the observer “pulling” the message from the observed object, a message system in Push mode is implemented.
From the class diagram of the observed object and observer, you can clearly see the message passing relationship between the two:
The observed object adds and removes an observer using the SUBSCRIBE and unsubscribe methods, and pushes messages to the observer using the broadcast method. The simplest observer mode pseudocode is shown below:
const clickHandler = (e) = > { / *... * / };
document.body.addEventListener('click', clickHandler);
Copy the code
Document. body in this case is the observed object, and the global object is the observer, who calls the clickHandler method when the Click event is triggered.
Implement an RXJs-style observer pattern
This article focuses not on how to use RxJS tools, but on understanding the basic implementation ideas. So instead of going into details about how to use RxJS, I’ll implement a simple observer pattern with similar functionality in order to learn how to use the observer pattern in my own work.
Let’s start with the basic usage of RxJS.
First, create an Observable:
var observable = Rx.Observable.create(observer= > {
observer.next(1);
observer.next(2);
setTimeout((a)= > {
observer.next(3);
observer.complete();
}, 1000);
});
Copy the code
2. Subscribe to an Observer on Observable:
const observer = {
next: x= > console.log('got value ' + x),
error: err= > console.error('something wrong occurred: ' + err),
complete: (a)= > console.log('done'),};console.log('just before subscribe');
const subscription = observable.subscribe(observer);
console.log('just after subscribe');
Copy the code
You should then see the output:
just before subscribe
got value 1
got value 2
just after subscribe
got value 4
done
Copy the code
If unsubscribe is called immediately after a call to SUBSCRIBE, the observer is unsubscribed:
subscription.unsubscribe();
Copy the code
The output is:
just before subscribe
got value 1
got value 2
just after subscribe
Copy the code
The execution action after 1s is cancelled.
As can be seen from the above code, RxJS not only uses the observer mode but also combines the use of the iterator mode. This paper focuses on the implementation principle of the observer mode.
There are two classes that need to be implemented, Observable and Subscription. An Observable subscribes to an observer using its subscribe method and returns a Subscription object. The Subscription object calls its own unsubscribe method to the current observer subscription and, as seen from the output, unexecutes the Observer’s next, error, and complete methods.
Note the observer does not need to implement, the observer is introduced to observables. Prototype. The parameters of the subscribe. After the analysis, this article will build a simplified version implementation based on the source code of RxJS.
First, observable.js:
module.exports = class Observable {
constructor(_subscribe) {
this._subscribe = _subscribe; // The execution function passed in
}
static create(_subscribe) {
return new Observable(_subscribe);
}
subscribe({ next, error, complete }) { // Destruct the incoming observer
const sink = new Subscription(next, error, complete);
this._subscribe(sink);
returnsink; }}Copy the code
Here’s the renewal.js:
module.exports = class Subscription {
constructor(next, error, complete) {
this._isStopped = false;
this._next = next;
this._error = error;
this._complete = complete;
}
next(value) {
if (!this._isStopped) {
this._next(value);
}
}
error(value) {
if (!this._isStopped) {
this._isStopped = true;
this._error(value);
this.unsubscribe();
}
}
complete(value) {
if (!this._isStopped) {
this._isStopped = true;
this._complete(value);
this.unsubscribe();
}
}
unsubscribe() {
this._isStopped = true; }}Copy the code
Subscription shows whether the unsubscribe method has been called through the _isStopped flag, and further wraps the Observer’s Next, error, and complete methods so that they can be unsubscribed.
Next, test with code similar to calling RxJS:
const Observable = require('./Observable');
const observable = Observable.create(observer= > {
observer.next(1);
observer.next(2);
setTimeout((a)= > {
observer.next(3);
observer.complete();
}, 1000);
});
const observer = {
next: x= > console.log('got value ' + x),
error: err= > console.error('something wrong occurred: ' + err),
complete: (a)= > console.log('done'),};console.log('just before subscribe');
const subscription = observable.subscribe(observer);
console.log('just after subscribe');
Copy the code
The output is the same as that of calling RxJS:
just before subscribe
got value 1
got value 2
just after subscribe
got value 4
done
Copy the code
If unsubscribe is called immediately after a call to SUBSCRIBE:
subscription.unsubscribe();
Copy the code
The output is:
just before subscribe
got value 1
got value 2
just after subscribe
Copy the code
This has implemented a suggested version of the RXJS-style Observer pattern, which is really Microsoft’s conscience and is recommended for every front-end engineer to master.
Practical use of the observer pattern
The value of observer mode is mainly reflected in the following two aspects:
-
Widely used in asynchronous programming.
Observer pattern to achieve the object of asynchronous operation logic and the complete separation of asynchronous callback logic, with the above mentioned document. The body. The addEventListener method as an example, the next use RxJS version of the observer pattern, first is to create the observables objects:
const observable = Rx.Observable.fromEvent(document.body, 'click'); Copy the code
Then subscribe to an Observer:
observable.subscribe({ next: (a)= > {/ *.. * /}})Copy the code
When writing asynchronous callback logic, we only need to pay attention to the subscribe function, and do not have to pay attention to the internal state of the asynchronous logic, and the purity of the program is improved.
-
The program is easy to expand.
Since the observer pattern provides a loose coupling between the object’s runtime logic and the callback logic, there is no need to change the observable’s internal implementation at all when a new observer appears. And you just need to call again observables. Prototype. The subscribe method adds a new observer, so for many cooperation projects, not only is advantageous for the division of labor can extend at any time.
Observer modelwithPublish/subscribeThe comparison of
The objective of the observer mode is the same as that of the publish/subscribe mode, in which the subject actively pushes messages to the notifying observer. However, there is some coupling between the subject and the observer in the observer mode, and the two must know the existence of each other to transmit messages.
The publish/subscribe mode is more like a global observer mode. The publish/subscribe mode introduces a message scheduling center between the subject and the observer, which is similar to EventEmitter in Node.js. The relationship between the subject and the observer is completely transparent, and all message transmission processes are completed through the message scheduling center. This means that the specific business logic code will be in the message dispatch center, and the complete loose coupling between the principal and the observer will result in a straightforward problem: the program will be significantly less readable.
The observer mode is inseparable from the publish/subscribe mode, but there are some differences, and each has its own advantages and disadvantages. It is not absolutely clear which one is better than the other. The mode to use should be selected according to the actual application scenarios.
The resources
- Observer pattern – Wiki
- 4 Javascript Design Patterns You Should Know
- Observer Design Patterns with examples
- Learn RxJS
- Building streaming applications – RxJS details