Yesterday we introduced what Subject is, and today we are going to talk about some applications of Subject and three other variations of Subject.

Subject

Yesterday we said that Subject is actually an instance of Observer Pattern, and that it manages a list of observers internally, iterating through the list and sending out values when it receives them, so we can use Subject like this

var subject = new Rx.Subject();

var observerA = {
    next: value= > console.log('A next: ' + value),
    error: error= > console.log('A error: ' + error),
    complete: (a)= > console.log('A complete! ')}var observerB = {
    next: value= > console.log('B next: ' + value),
    error: error= > console.log('B error: ' + error),
    complete: (a)= > console.log('B complete! ')
}

subject.subscribe(observerA);
subject.subscribe(observerB);

subject.next(1);
// "A next: 1"
// "B next: 1"
subject.next(2);
// "A next: 2"
// "B next: 2"Copy the code

JSBin

Here we can directly use the next method of the subject to transmit values, which will be received by all subscribing observers. Since the Subject itself is an Observable, this method is suitable for some front-end frameworks that cannot use observables directly. For example, React wants to listen on DOM events

class MyButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count: 0 };
        this.subject = new Rx.Subject();

        this.subject
            .mapTo(1)
            .scan((origin, next) = > origin + next)
            .subscribe(x= > {
                this.setState({ count: x })
            })
    }
    render() {
        return <button onClick={event= > this.subject.next(event)}>{this.state.count}</button>}}Copy the code

JSBin | JSFiddle

As can be seen from the above code, if we want to customize events with React, we cannot directly use the Creation operator of an Observable to create an Observable. This can be done by relying on Subject.

Since Subject is both an observer and an Observable, it has a wide range of applications. In addition, the multicase feature described in the previous article will be introduced in more applications in the next article. Let’s take a look at three variations of Subject.

BehaviorSubject

Most of the time we want the Subject to represent the current state, rather than the single event sent. That is, if a new subscription is made today, we want the Subject to immediately give the latest value, rather than no response, as in the example below

var subject = new Rx.Subject();

var observerA = {
    next: value= > console.log('A next: ' + value),
    error: error= > console.log('A error: ' + error),
    complete: (a)= > console.log('A complete! ')}var observerB = {
    next: value= > console.log('B next: ' + value),
    error: error= > console.log('B error: ' + error),
    complete: (a)= > console.log('B complete! ')
}

subject.subscribe(observerA);

subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"

setTimeout((a)= > {
    subject.subscribe(observerB); // Wait 3 seconds to subscribe, observerB will not receive any value.
},3000)Copy the code

In this example, after observerB subscribes, no element is sent to observerB because no subject.next() is executed after that, but many times we want the subject to express the current state, Instead of waiting for a change to receive a new state when you subscribe, for example, we want a BehaviorSubject like observerB to subscribe and get a 3 immediately.

The main difference between a BehaviorSubject and a Subject is that the BehaviorSubject presents the current value instead of just sending an event. The BehaviorSubject remembers the last element it sent and treats that element as the current value. Using the BehaviorSubject constructor, you need to pass in a parameter to represent the initial state of the BehaviorSubject

var subject = new Rx.BehaviorSubject(0); // 0 is the starting value
var observerA = {
    next: value= > console.log('A next: ' + value),
    error: error= > console.log('A error: ' + error),
    complete: (a)= > console.log('A complete! ')}var observerB = {
    next: value= > console.log('B next: ' + value),
    error: error= > console.log('B error: ' + error),
    complete: (a)= > console.log('B complete! ')
}

subject.subscribe(observerA);
// "A next: 0"
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"

setTimeout((a)= > {
    subject.subscribe(observerB); 
    // "B next: 3"
},3000)Copy the code

JSBin | JSFiddle

As you can see from the above example, a BehaviorSubject needs to be given a state when it is built, and any subsequent subscription will send the latest state first. In fact, this behavior is an expression of a state rather than a single event, just like age and birthdays, age is a state and birthdays are events; So when we want to express age with a stream, we should use BehaviorSubject.

ReplaySubject

There are times when we want Subject to represent events, but we can resend the last few elements in a new subscription, so we can use ReplaySubject, as shown in the following example

var subject = new Rx.ReplaySubject(2); // Repeat the last two elements
var observerA = {
    next: value= > console.log('A next: ' + value),
    error: error= > console.log('A error: ' + error),
    complete: (a)= > console.log('A complete! ')}var observerB = {
    next: value= > console.log('B next: ' + value),
    error: error= > console.log('B error: ' + error),
    complete: (a)= > console.log('B complete! ')
}

subject.subscribe(observerA);
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"

setTimeout((a)= > {
    subject.subscribe(observerB);
    // "B next: 2"
    // "B next: 3"
},3000)Copy the code

JSBin |

Some people might think that ReplaySubject(1) is equivalent to BehaviorSubject, but the BehaviorSubject has a start value when it is being built. For example, BehaviorSubject(0) has a start value of 0. The BehaviorSubject was a state and the ReplaySubject was just a replay of the event.

AsyncSubject

AsyncSubject is one of the oddest transformations, which is a bit like operator last and will send the last value after the subject ends, as shown in the following example

var subject = new Rx.AsyncSubject();
var observerA = {
    next: value= > console.log('A next: ' + value),
    error: error= > console.log('A error: ' + error),
    complete: (a)= > console.log('A complete! ')}var observerB = {
    next: value= > console.log('B next: ' + value),
    error: error= > console.log('B error: ' + error),
    complete: (a)= > console.log('B complete! ')
}

subject.subscribe(observerA);
subject.next(1);
subject.next(2);
subject.next(3);
subject.complete();
// "A next: 3"
// "A complete!"

setTimeout((a)= > {
    subject.subscribe(observerB);
    // "B next: 3"
    // "B complete!"
},3000)Copy the code

JSBin |

As can be seen from the above code, AsyncSubject will send the last value after the end of the subject. In fact, this behavior is very similar to Promise, which sends a value after the end of the event. However, in practice, AsyncSubject is rarely used. Most of the time, you use BehaviorSubject and ReplaySubject orSubject.

Let’s just keep AsyncSubject deep in the brain

Today’s summary

Today, I introduced some application modes of Subject, as well as the characteristics of BehaviorSubject, ReplaySubject and AsyncSubject deformation respectively. I wonder if there is any benefit for readers? If you have any questions, feel free to leave a comment below!