DEV Community

Cover image for When Use RxJS Subject, BehaviourSubject, ReplaySubject, AsyncSubject, or Void Subject in Angular
Dev By RayRay
Dev By RayRay

Posted on • Originally published at hasnode.byrayray.dev

When Use RxJS Subject, BehaviourSubject, ReplaySubject, AsyncSubject, or Void Subject in Angular

Angular has many types of Observables which you can use. Maybe you've seen Subject, BehaviourSubject, ReplaySubject, or AsyncSubject in Angular examples and wondering what they are and when you can use them.

In this post, I want to dive deeper into what those types of Subjects are and when you should use them. So buckle up and enjoy the ride.

divider-byrayray.png

Table Of Contents

divider-byrayray.png

What is a Subject?

RxJS is responsible for the reactivity in Angular. A Subject is a particular type of Observable from the RxJS library.

If you don't know what an Observable is, check this post by "Understanding RxJS Observables and why you need them" on the LogRocket blog.

Unicast

Unicast example

An Observable is unicast.
An Observer and its Subscriber have a one-to-one relationship. Each subscribed Observer owns an independent execution of the Observable.

Multicast

Multicast example

In comparison to a regular Observable, a Subject allows values to be multicasted to many Observers. A Subject and its subscribers have a one-to-many relationship.

A Subject can be an Observable as well as an Observer. They hold a registry of many listeners to multiple Observables.

divider-byrayray.png

Observable VS Subject in Code

An Observable and Subject share their API. Both of them have the same methods and how you create them. But they behave very differently from each other.

Observable in Code

import { Observable } from "rxjs"

const observable = new Observable(subscriber => {
    subscriber.next(1);
    subscriber.next(2);
    subscriber.next(3);
    subscriber.complete();
});

console.log('just before subscribe');

// Subscriber 1
observable.subscribe({
  next(x) { console.log('sub1: got value ' + x); },
  error(err) { console.error('sub1: something wrong occurred: ' + err); },
  complete() { console.log('sub1: done'); }
});

// Subscriber 2
observable.subscribe({
  next(x) { console.log('sub2: got value ' + x); },
  error(err) { console.error('sub2: something wrong occurred: ' + err); },
  complete() { console.log('sub2: done'); }
});

console.log('just after subscribe');
Enter fullscreen mode Exit fullscreen mode

Working example

Here, you can see that the data is sent to the first subscriber and will finish before it continues to the next subscriber.

In the RxJS documentation, they are describing that "Each call to observable.subscribe triggers its independent setup for that given subscriber".

That's why every subscriber is running independently from each other. But the RxJS team offers a way to create "multicasted Obsevables."

Subject in Code

import { Subject } from "rxjs"

const subject = new Subject();

 // Subscriber 1
subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);

// Subscriber 2
subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(2);
Enter fullscreen mode Exit fullscreen mode

Working example

With the Subject, you can see that the Subject takes the lead. It sends messages to both subscribers instead of waiting. In my opinion, this clearly shows the difference between a regular Observable and a Subject.

The RxJS documentation says the following about subscribing to a Subject.

Internally to the Subject, subscribe does not invoke a new execution that delivers values. It simply registers the given Observer in a list of Observers, similarly to how addListener usually works in other libraries and languages.

divider-byrayray.png

Subject

We know that a Subject is an Observable. But instead of sending information to one subscriber, they can send their data to multiple subscribers simultaneously (they multicast).

A Subject has three methods which you can use.

  • subscribe with this method, you can activate the subscription of a new subscriber.
  • next with this method, you can pass new values. All the current subscribers will receive this.
  • complete with this method, you close all the subscriptions to the Subject.

A vital detail is that a Subject doesn't have an initial value. Every value passed with the next method will send the values to all the subscribers.

But if the value is already sent before a subscriber is subscribed, it won't receive that data. (Click the "run" button to see it working)

const rxjs = require('rxjs');
const { Subject } = rxjs

const subject = new Subject();

 // Subscriber 1
subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);

// Subscriber 2
subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(2);
Enter fullscreen mode Exit fullscreen mode

Working example

divider-byrayray.png

BehaviourSubject

The BehaviourSubject is a variant of the Subject. This variant knows about the current value, which a normal Subject doesn't.

When there has already been sent data to the current subscribers, this Subject becomes very useful. But another subscriber get's introduced at a later moment. Sometimes you want to pass the current value to that subscriber. With the BehaviourSubject you can do that. (Click the "run" button to see it working)

import { BehaviorSubject } from "rxjs"

const subject = new BehaviorSubject(0); // 0 is the initial value

subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);
subject.next(2);

subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(3);
Enter fullscreen mode Exit fullscreen mode

Working example

So use the BehaviourSubject to give a subscriber the last known value of the Observable. But, what if you want a bit more than the previous value?

divider-byrayray.png

ReplaySubject

The ReplaySubject does what it says. It can replay a fixed amount of values to new subscribers.

Think of an online playlist that a DJ is playing. But you want to go back in that stream. The ReplaySubject can make sure you can revert three tracks and start listening from there. (Click the "run" button to see it working)

import { ReplaySubject } from "rxjs" 

const subject = new ReplaySubject(2); // buffer 3 values for new subscribers

subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);
subject.next(2);
subject.next(3);

subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(4);
subject.next(5);
Enter fullscreen mode Exit fullscreen mode

Working example

As you can see, at the creation of the ReplaySubject(2), I passed the number 2, which tells the Subject that it needs to send the last two values to every new subscriber.

When that new subscriber received the passed values, it will stay in sync with the other subscriber, which is excellent.

But to make sure that the ReplaySubject(10000) won't pass constant values to every new subscriber, we can give it a time limit. The example below defines that it can keep a hundred values in memory and pass it to new subscribers, but those values are valid for 500 milliseconds.

const subject = new ReplaySubject(100, 500);
Enter fullscreen mode Exit fullscreen mode

This feature gives a lot of possibilities, so be smart with it.

divider-byrayray.png

AsyncSubject

When I saw the AsyncSubject and saw that it only sends the latest value to subscribers when it's completed, I thought, "why would I want to use this?". Until I saw this post on Medium.

So this gave an idea that an AsyncSubject is a great candidate for Ajax requests. Because with most GET requests, you're only going to wait for one response, right.

import { AsyncSubject } from "rxjs"

const subject = new AsyncSubject();

subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);

subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(5);
subject.complete();
Enter fullscreen mode Exit fullscreen mode

Working example

When you click the "run" button above, you will see that the AsyncSubject will pass multiple values, but only the last value before the complete() method is called will give to the subscribers.

divider-byrayray.png

Void Subject

In most of the scenarios where you use a Subject with subscribers, it's relevant that you get access to the value that has passed. But what if you don't need an actual value but only want to hook into the event and don't need a value. That's when you use a void subject.

The default behavior for a Subject is just that. (Click the "run" button to see it working)

import { Subject } from "rxjs"

const subject = new Subject(); // Shorthand for Subject<void>

subject.subscribe({
  next: () => console.log('One second has passed')
});

setTimeout(() => subject.next(), 1000);
Enter fullscreen mode Exit fullscreen mode

Working example

divider-byrayray.png

Conclusion

Let's wrap this up and conclude when you need a regular Observable or one of the Subject types.

Use a Observable when..

A regular Observable should be used when you only need one subscriber. Or you don't care that the subscriber that comes first will be finished first until the second will get its values.

Use a Subject when..

When you need multiple subscribers and care that all the subscribers are getting their new values simultaneously, you need a Subject.

  • Use a BehaviourSubject when you need the last given value.
  • Use a ReplaySubject when you need more than the last given value. (For example, the previous five values) Or you want to set a time window for the values can be validly sent to subscribers.
  • Use an AsyncSubject when you only want the last value to be passed to the subscribers.
  • Use a Void Subject if you don't want to pass any value but just want to hook into the event.

Hopefully, this will help you make the right choice!

divider-byrayray.png

Thanks!

hashnode-footer.png
*I hope you learned something new or are inspired to create something new after reading this story! πŸ€— If so, consider subscribing via email (scroll to the top of this page) or follow me here on Hashnode.
*

Did you know that you can create a Developer blog like this one, yourself? It's entirely for free. πŸ‘πŸ’°πŸŽ‰πŸ₯³πŸ”₯

If I left you with questions or something to say as a response, scroll down and type me a message. Please send me a DM on Twitter @DevByRayRay when you want to keep it private. My DM's are always open 😁

Top comments (8)

Collapse
 
mauromattos00 profile image
Mauro Mattos

Incredible! My main stack in Front End is Angular and before this article, I could only understand Observables. Now all of them make sense to me and I have a bunch of ideas of how to use them. Thanks a million for this excellent work!

Collapse
 
devbyrayray profile image
Dev By RayRay

Thanks a lot for your comment! I love to read that it now made sense for you 😁 This is always the goal with my blogs, to translate complex topcis into easy to understand blogs. So everyone can understand it ❀️

Collapse
 
pix303 profile image
Paolo Carraro

Thanks a lot! Give me a more comple comprehension of obs

Collapse
 
devbyrayray profile image
Dev By RayRay

πŸ˜πŸ‘

Collapse
 
techygeeky profile image
Kapil RaghuwanshiπŸ–₯

What a comparison! Quick, Crisp, to the point🀟

Collapse
 
devbyrayray profile image
Dev By RayRay

Thanks for such a positive comment! There are coming more posts on RxJS, and Angular so keep an πŸ‘€ out.

Collapse
 
nombrekeff profile image
Keff

I'm enjoying this series. Really well explained! I needed to refresh my knowledge on this a bit.

Collapse
 
devbyrayray profile image
Dev By RayRay

Thanks man! It makes me happy that you like it 😁