Services in Angular: Subject vs BehaviorSubject
RxJS is a library used in Angular applications to manage asynchronous data streams. It introduces the concept of Observables and Subjects to handle data flow. This article will discuss two types of Subjects:
Subject
andBehaviorSubject
, what they are, and how they differ.
Discover how at OpenReplay.com.
To get started, we’ll first need to understand the concept of angular services and observables, which is the key to understanding Subject
and BehaviorSubject
.
The prerequisites for this article are:
- Node installed
- Angular CLI
- Understanding of JavaScript
- An already set up Angular project. If you don’t have one, create one.
To do this, navigate to the folder where you want your project installed and run this command:
ng new my_first_project
Follow the steps shown in the terminal to complete the installation.
cd
to the just installed project; in my own case it is my_first_project:
cd my_first_project
Start up the development server by running this command:
ng -serve
You should have something like this up in your browser:
Now, let’s head back to our tutorial proper.
What Are Angular Services?
Angular services are the foundation of a well-structured and modular Angular application. These services act as singletons, serving as a centralized and shareable source for data, logic, and functionality across various components. Angular supports a modular architecture that improves code reusability and maintainability by enclosing individual business logic or data activities within services.
RxJS plays a significant role in Angular services. It provides a unified approach to dealing with asynchronous data and events. Instead of using promises for one-time events and callbacks for multiple values, RxJS Observables can handle various asynchronous operations.
What Are Observables?
Observables are the foundation of RxJS. They are lazy collections of multiple values over time, allowing for subscribing to data streams and responding to emitted values.
Observables are useful for handling events, asynchronous requests, and handling multiple values. They’re ideal for scenarios like HTTP requests, user inputs, and other event-based operations. Below is an example of how they work:
import { Observable } from 'rxjs';
const observable = new Observable(subscriber => {
subscriber.next('Hello');
subscriber.next('World');
subscriber.complete();
});
observable.subscribe({
next: x => console.log(`Got value ${x}`),
error: err => console.error(`Something wrong occurred: ${err}`),
complete: () => console.log('Done'),
});
In the code above, an observable is created, emitting two values, ‘Hello’ and ‘World,’ and then completes. When one value is emitted, the next
callback is triggered, logging the received value with the message “Got value”. After both values are emitted, the observable completes, invoking the complete
callback and logging “Done “. If we were to have an error during the observable’s lifecycle, it would be handled by the error
callback.
What are Subjects?
According to the documentation, A Subject
is a special type of observable that allows values to be multicasted to many observables. In simple terms, they are special containers that can hold values, and when a new value comes into a subject, that value can be shared with numerous observers at once.
Imagine you have a central hub (the Subject
) that can send out messages to multiple people (observers) at the same time. When something new happens, everyone connected to the hub gets notified. This makes Subjects handy in certain programming scenarios, especially in the context of reactive programming with libraries like RxJS. Let’s look at an example of a standard subject
.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { Subject } from 'rxjs';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
subject = new Subject();
constructor() {
this.subject.subscribe((res) => {
console.log('first subscriber', res);
});
}
}
In the code above, an instance of the RxJS Subject
class is created and named subject
. Subsequently, a subscriber is set up in the component’s constructor to listen for emissions from this Subject
. However, upon checking the console in the browser, no output is observed since there has been no emission on the Subject
at this point.
This is because although we have an observable, there is no emission on the observable, so let’s try to emit a value for a subject.
Right after this. Subject.subscribe function
, add this:
this.subject.next('emission 1');
Whenever we emit this, it will emit the value to the subscriber who subscribed before this emission. So, in our case, the emission one will be emitted to the particular subscriber, the “first subscriber”. We’ll see that in our console if we check the result in our browser. It displays that subscriber one gets the emission one.
This means that by default, we are not getting the value, and whenever we are emitting, only subscribers who have the subscription before emission receive the values.
Difference Between Observables and Subjects
Subjects are also a type of observer; the main difference is that in observers, we need a separate observer interface to feed an observable source, but subjects implement both the observer and observable interfaces. In other words, subjects can be both consumers and providers, while observables can be only consumers.
BehaviorSubject
BehaviorSubject
is a type of Subject
in RxJS that extends the basic Subject
class. The key feature of a BehaviorSubject
is it requires a default value and remembers the last emitted value. When a new subscriber subscribes to a BehaviorSubject
, it immediately receives the last emitted value or the initial value if no values have been emitted yet.
A BehaviorSubject
object requires a default value. Using the example we used in objects:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { BehaviorSubject, Subject } from 'rxjs';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
behaviorSubject = new BehaviorSubject('');
constructor() {
this.behaviorSubject.subscribe((res) => {
console.log('First subscriber', res);
});
}
}
In the code above, a subscription is established to the behaviorSubject within the component’s constructor. When the behaviorSubject
emits a value, the provided callback function logs a message to the console, indicating that the first subscriber received the emitted value.
If we check the result in the browser, we see that the first subscriber is empty because the behaviorSubject will emit the value once you subscribe. This means that once you subscribe, the default value or the previous value will be triggered immediately. So let’s add emission this time. After this.behaviorSubject.subscribe function, add this:
this.behaviorSubject.next('emission 1');
What happens is that it will first emit the default value and then emit the value we just added:
So, how does it behave when we have two subscribers? Let’s see that by adding another subscriber to the code. Update the constructor code to this:
constructor() {
this.behaviorSubject.subscribe((res) => {
console.log('first subscriber', res);
});
this.behaviorSubject.next('emission 1');
this.behaviorSubject.subscribe((res) => {
console.log('second subscriber', res);
});
}
If you check the result in the browser, you’ll see that the ‘first subscriber’ got two emissions and the second subscriber got one emission.
We’ll also experience something similar if we add another emission to it. The first subscriber will be emitted three times, while the second emission will be emitted three times. This is because behaviorSubject will emit the values since you are subscribed to it, and it will emit the default values if no emission is happening or added.
Differences between Subject and BehaviorSubject
The main differences between Subject
and BehaviorSubject
lie in their handling of emitted values and subscriptions.
Subject
does not have default values and will not have the previous value, whereas behaviorSubject will hold the previous value. Also, in Subject
, once you emit the value, it will give the value to the subscriptions before the emission, whereas behaviorSubject will emit the values to all the subscribers.
Another difference is that BehaviorSubject
is about maintaining the latest state, whereas Subject
is about current value emission.
Subject
is more suited for one-off or event-driven emissions where the latest state is not required. When you need continuous access to the current or most recent value, use BehaviorSubject
.
Conclusion
In this article, we looked at Angular services, RxJS, and observables. We also looked at Subject
and BehaviorSubject
, how they work, and their differences. The type of subjects is not just limited to Subject
and behaviorSubject alone as there are other types of subjects: RelaySubject and AsyncSubject.
When dealing with Angular and RxJS, understanding the distinctions between Subject
and BehaviorSubject
is critical. Knowing how they handle emitted values and subscriptions lets you choose the right type of Subject
for your specific use case. Remember always to consider the characteristics and needs of your data flow when choosing between Subject
and BehaviorSubject
.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.