RxJS Observables versus Subjects
Cory Rylan
- 4 minutes
In this post, we are going to compare the different types of Observables the RxJS Observable library provides. RxJS an Observable stream library is commonly used in Angular projects. RxJS also can be a source of confusion or a learning curve when starting out in Angular. With RxJS not only does it provide a Observable class for us to use but also several subtypes with different properties. If you are new to RxJS, I suggest taking a look at this video Reactive Programming with RxJS and Angular.
Observables
To get started we are going to look at the minimal API to create a regular Observable. There are a couple of ways to create an Observable. The way we will create our Observable is by instantiating the class. Other operators can simplify this, but we will want to compare the instantiation step to our different Observable types.
import { Observable } from 'rxjs';
const observable = new Observable(observer => {
setTimeout(() => observer.next('hello from Observable!'), 1000);
});
observable.subscribe(v => console.log(v));
We import Observable
from the rxjs
package. To create our Observable, we instantiate the class. The class constructor expects a function as its parameter. The Observable will pass in an observer object. This observer object is what we use to trigger events for our Observable to emit. In this simple use case, our Observable will trigger an event with the value 'hello from observable' after a one-second delay. It is not common to create Observables like this since there are operators built into RxJS that can shorten down this code. Also if you are using RxJS in Angular, you are likely getting an Observable from the framework.
Notice in our example the observer object is scoped to the constructor. We cannot access the observer and call .next()
outside of the internal implementation of the Observable. This scoping ensures only the Observable knows how and when the events should be emitted for subscribers of our Observables.
Observables by default are "Cold" meaning they are lazy and won't run any code until there is a subscriber. Observables also by default do not share their work between subscribers. For example, our use case if I subscribe three times, I will have three setTimeout
s created. Not usually what we expect when we first start using RxJS. Let's look at a different Observable subtype that diverges from this behavior.
Subjects
The subject is another Observable type in RxJS. Subjects like Observables can emit multiple event values. However, Subjects allow subscribers of the Subject to push back or trigger their own events on the Subject. Here is what the Subject API looks like,
import { Subject } from 'rxjs';
const subject = new Subject();
subject.next('missed message from Subject');
subject.subscribe(v => console.log(v));
subject.next('hello from subject!');
We instantiate the Subject class. With the Subject instance, we can immediately trigger events outside of the constructor by calling next()
. Now anyone can listen or trigger events on the Subject. Notice how we call next and emit 'missed message from Subject' before we have subscribed to the Subject? Subjects, unlike regular Observables, are what we would call "Hot". A hot Observable is an Observable that can start emitting events before you subscribe. This means you can miss previous events that have already emitted.
Subjects, unlike Observables, share their work with all subscribers. Unlike our first Observable that created a setTimeout
for each subscriber, this Subject would share that work with all subscribers. What if we subscribe late to our Subject and want to get the previous value we missed? Well, that's where our next Subject type comes in, the ReplaySubject.
ReplaySubject
The ReplaySubject like a regular subject can have events triggered outside the constructor as well as being a hot Observable. Unlike the regular Subject ReplaySubject will replay the last event emitted if you subscribe late to the ReplaySubject. Let's take a look at what that looks like,
import { ReplaySubject } from 'rxjs';
const replaySubject = new ReplaySubject();
replaySubject.next('hello from ReplaySubject!');
replaySubject.next('hello from second event from ReplaySubject!');
replaySubject.subscribe(v => console.log(v));
Notice the API of ReplaySubject is very similar to Subject. In this case, we don't miss the first event. The ReplaySubject replays the last value emitted we had missed. In our subscription, we get the value 'hello from the second event from ReplaySubject!' from our ReplaySubject. Before we wrap up, we have one more Subject type I want to cover, the BehaviorSubject.
BehaviorSubject
The BehaviorSubject builds on top of the same functionality as our ReplaySubject, subject like, hot, and replays previous value. The BehaviorSubject adds one more piece of functionality in that you can give the BehaviorSubject an initial value. Let's go ahead and take a look at that code.
import { ReplaySubject } from 'rxjs';
const behaviorSubject = new BehaviorSubject(
'hello initial value from BehaviorSubject'
);
behaviorSubject.subscribe(v => console.log(v));
behaviorSubject.next('hello again from BehaviorSubject');
As you can see the constructor of the BehaviorSubject can take an initial value. This initial value will be replayed to any subscribers until a new value is emitted then the new value will be replayed for all new subscribers. BehaviorSubject is a fairly common subject to use in application with reactive programming styles where we want to have some central state/information shared throughout our code. See Angular Observable Data Services for more details.
RxJS has many different Observable types, each with their unique functionality. Full working code examples can be found in the link below.