RxJS: Observable vs Subject - Tutorial for Beginners
Intro to RxJS Observable vs Subject
RxJS is one of the most useful and the most popular libraries when using Angular as the main framework for your project. RxJS provides two types of Observables, which are used for streaming data in Angular.
For most beginners, who just started with Angular, Observables are the biggest source of frustration; it’s confusing and not easy to understand. Personally, I felt the same; when I started with RxJS, it was confusing.
That’s why I’d decided to create an article where I’ll go through the RxJS library and will describe the most important concepts, with a big focus on Observables ad Subjects. I’ll explain how it works, why it’s good to use it, and what is the difference between Observable and Subject.
Let’s start!
What is RxJS?
RxJS is a library supporting reactive programming, very often used with an Angular framework. It provides an Observable class that helps to compose asynchronous and event-based programs. Besides Observable, RxJS comes with operators for handling asynchronous events.
What makes RxJS more powerful is producing values using the pure function, and because of that, the code is less liable to errors.
The most important concepts in RxJS for asynchronous event handling are Observables, Observers, Subjects, Subscriptions, Operators, and Schedulers. In the next paragraphs, I’m going to explain to you the most important ones, what they are and what’s their role in the asynchronous event management.
Push and pull model
Before I’ll explain what is Observable and what is Subject, let me tell you about two communication protocols between data producer and data consumers.
Push model
In the push model, the most important is data producer. In this case, data producers decide when to send values to data consumers, and data consumers have no idea when data will come.
This model is used in Promises, where the promise is a data producer, which is sending data to the callback. Callback doesn’t know when it will receive data, and it relay totally on the data producer.
It’s similar to the Observables.
Pull model
Inside the pull model, it works another way. Here, the most important is data consumer, and it decides when it wants to get data from the data producer. In this model, data producers have no decision power about delivering data.
To imagine the pull model, we can think about the function that returns some value, and the function is a data producer in this case. It doesn’t decide when the data will be returned or send. The data consumer in this case
What is Observable?
I found out about Observables when I started to learn Angular, although it’s not an Angular feature. It was introduced as the main concept of the RxJS library, supporting reactive programming.
Observable is a new way of handling asynchronous requests, just like Promises or callbacks. Concerning push and pull models, Observables is a push collection of multiple values.
Observable pass four stages during their lifecycle: creation, subscription, execution, and destruction.
Now, let’s go through all of them and understand what’s going on behind the Observable.
Create an Observable
There are many ways to create Observables, but the most common is using new Observable
or Observable.create()
methods. Let’s take a look at the code below.
import { Observable } from 'rxjs';
const myObservable = new Observable(observer => {
setTimeout(() => {
observer.next('Hello world!');
}, 2000);
});
I’ve created a new Observable in this code example and assigned it to the myObservable
constant. This Observable will emit the string Hello world!
every two seconds to a subscriber.
Observable class constructor takes a function as a parameter, and that function has an observer
object inside.
What is Observer
The observer
is a consumer of values delivered by the Observable. To better understand the Observer
, let’s take a look at the simple observer’s code example.
const observer = {
next: value => console.log('Observer got the next value:' + value),
error: error => console.log('Observer got an erro:r' + error),
complete:() => console.log('Observer got a complete notification'),
};
In the code example, you can see the observer
object with three values: next, error and complete, and a callback with the value for each type of the notification.
Now, when we created an Observable,
and we know what’s the observer
, let’s find out what’s subscription
.
Subscribe to Observable
A subscription is an object that represents a disposable resource. When the Observable is executed, the subscription gets new resources. Subscription has one important method .unsubscribe()
and it doesn’t take any params; it just removes values kept in the Subscription object.
To make our Observable working, we have to subscribe to it, using .subscribe()
method. We can compare subscribing Observable, to calling the function. We can pass the observer
object as a parameter of the .subscribe
method. Let’s take a look at the code below.
myObservable.subscribe(observer);
// Result
Observer got the next value: Hello world!
In the code above, I used a .subscribe()
method with myObservable
to make it working and start the execution of our Observable.
Executing Observable
The execution of the Observable starts when the Observable is subscribed. The execution provides multiple values over time, and it can be done synchronously and asynchronously. Observable execution can provide three types of notifications: next, which sends a value error, which returns an error complete, which doesn’t send a value
Disposing Observable
Although the Observable can be executed infinitely, there’s an option to stop the execution after it’s done to not wasting computation power. To stop the execution of the observable, we have to unsubscribe. Let’s take a look at the code below to see how it’s done.
myObservable.unsubscribe();
It’s very easy, and it’s just using and .unsubscribe()
method on our Observable.
What is Subject?
Right now, let’s go to the second important concept of RxJS, which is the Subject. The Subject is another type of Observable, and it allows value to be consumed by many Observers, not like in the normal Observable just by one. This means that Subjects are multicast, and Observables are unicast.
Every Subject is an Observable, and it’s possible to subscribe to it, but the subscribe method doesn’t invoke the new execution. It just registers a new Observer to the list of Observers.
Every Subject is an Observer, which means it has next
, complete
, and error
methods. When you want to add new data to the Subject, you have to use the .next()
method, then the value would be multicasted to all Observers.
Let’s take a look at the Subject code example.
import { Subject } from 'rxjs';
const mySubject = new Subject();
mySubject.subscribe({
next: (value) => console.log('First observer:' + ${value})
});
mySubject.subscribe({
next: (value) => console.log('Second observer:' + ${value})
});
mySubject.next('Hello');
mySubject.next('Bye');
// Result
First observer: Hello
Second observer: Hello
First observer: Bye
Second observer: Bye
In the code, I’ve started by importing Subject
from RxJS, then I created a new Subject and assigned it to mySubject
constant.
Next, I subscribed to mySubject
twice, and after that, I passed two values with .next()
method. Below that you can see how the data stream would look like. First, both observers will return the first value, and next both observers will return second value.
Now, we have a basic understanding of what is Subject, so we can go through three different types of Subjects.
Behavior Subject
The first and the most popular is the Behavior Subject. This type of Subject keeps the last value emitted to the data consumer, and if we will subscribe to new Observer
to the Behavior Subject, it will receive that value immediately. We can also pass the initial value to the Behavior Subject when we define it.
Let’s take a look at the code example to understand it better.
import { BehaviorSubject } from 'rxjs';
const mySubject = new BehaviorSubject('Hi');
mySubject.subscribe({
next: (value) => console.log('First observer:' + ${value})
});
mySubject.next('Hello');
mySubject.subscribe({
next: (value) => console.log('Second observer:' + ${value})
});
mySubject.next('Bye');
// Result
First observer: Hi
First observer: Hello
Second observer: Hello
First observer: Bye
Second observer: Bye
In the code above, you can see that at first only First observer returns values. It returns the initial value „Hi”, then it returns the second value, „Hello”.
Next, we subscribe to the Subject once again, and the newly created Observer gets the last emitted value, „Hello”. In the end, both subscribes get the last value, „Bye”.
Reply Subject
Reply Subject is the next typo of Subject, and it’s very similar to the Behavior Subject, but it can record multiple values from previous executions and pass those values to the new Observers. When we create a new Reply Subject, we have to specify how many values we want to memorize. Besides that, we can also specify the time in milliseconds, which will determine how old the memorized values should be.
Let’s take a look at the code to understand it better.
import { ReplySubject } from 'rxjs';
const mySubject = new ReplySubject(2);
mySubject.subscribe({
next: (value) => console.log('First observer:' + ${value})
});
mySubject.next('Hey');
mySubject.next('Hi');
mySubject.next('Hello');
mySubject.subscribe({
next: (value) => console.log('Second observer:' + ${value})
});
mySubject.next('Bye');
// Result
First observer: Hey
First observer: Hi
First observer: Hello
Second observer: Hi
Second observer: Hello
First observer: Bye
Second observer: Bye
In the code above, we define a new ReplySubject
, and we want it to keep two last emitted values. Next, we create a new Observer
, and we add three values. First Observer stream value „Hey”, „Hi”, „Hello”, and then we create the Second Observer. The ReplySubject
has to remember two last values. So, the Second Observer immediately gets the value „Hi” and „Hello”. When the next value is added, then both Observers return now just one value „Bye”.
Async Subject
The last type is Async Subject, and it keeps only the last value of the execution, and it sends that value to the Observer only when the execution is completed, which means that .complete()
method needs to be called.
Let’s take a look at the code.
import { AsyncSubject } from 'rxjs';
const mySubject = new AsyncSubject();
mySubject.subscribe({
next: (value) => console.log('First observer:' + ${value})
});
mySubject.next('Hey');
mySubject.next('Hi');
mySubject.subscribe({
next: (value) => console.log('Second observer:' + ${value})
});
mySubject.next('Bye');
mySubject.complete();
// Result
First observer: Bye
Second observer: Bye
In the code example, you can see that only the last value before the .complete()
method is returned to the Observer, and both First Observer and Second Observer return the same value „Bye”.
Difference between Observables and Subjects
When we have an overview of what the Observable is and what is the Subject in RxJS, let’s try to find some differences between.
There are a few most significant differences between Observables and Subject. First of all, Observables can’t be data consumers, they are just data providers, but Subjects can be both consumers and providers. Observables are passive subscribers to the events, and they don’t generate anything on their own, when Subjects can trigger new events with available methods like .next()
or .complete()
.
Another important difference is in firing events. When we have more than one subscriber on the channel, there are two ways of handling events. In one case, all subscribers get the same event, and it’s the case of Subjects, but in Observables, we can get a different result on each Observer, because subscribers get another instance of the event.
Conclusion
In this article, we went through a lot of interesting concepts. Starting from what is RxJS library, through push and pull models, to a deeper explanation of Observables and Subjects.
I lead you through what is Observable, how it works, and what four stages it has. Also, I showed you some code, so you can understand it even better. Next, I went to the general Subject explanation, and also to the explanation of each Subject type. Although they are very similar, I showed you some code so you can visualize the differences.
I hope you’ll find this article useful, especially when you start learning Angular with RxJS, or you just would like to clarify these confusing concepts which Observables and Subjects are.
Thank you for reading,
Anna