I'm not clear on the difference between a Subject
and a BehaviorSubject
. Is it just that a BehaviorSubject
has the getValue()
function?
A BehaviorSubject holds one value. When it is subscribed it emits the value immediately. A Subject doesn't hold a value.
Subject example (with RxJS 5 API):
const subject = new Rx.Subject();
subject.next(1);
subject.subscribe(x => console.log(x));
Console output will be empty
BehaviorSubject example:
const subject = new Rx.BehaviorSubject(0);
subject.next(1);
subject.subscribe(x => console.log(x));
Console output: 1
In addition:
BehaviorSubject should be created with an initial value: new Rx.BehaviorSubject(1)
Consider ReplaySubject if you want the subject to get previously publised values.
BehaviourSubject
BehaviourSubject will return the initial value or the current value on Subscription
var bSubject= new Rx.BehaviorSubject(0); // 0 is the initial value
bSubject.subscribe({
next: (v) => console.log('observerA: ' + v) // output initial value, then new values on `next` triggers
});
bSubject.next(1); // output new value 1 for 'observer A'
bSubject.next(2); // output new value 2 for 'observer A', current value 2 for 'Observer B' on subscription
bSubject.subscribe({
next: (v) => console.log('observerB: ' + v) // output current value 2, then new values on `next` triggers
});
bSubject.next(3);
With output:
observerA: 0
observerA: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3
Subject
Subject does not return the current value on Subscription. It triggers only on .next(value)
call and return/output the value
var subject = new Rx.Subject();
subject.next(1); //Subjects will not output this value
subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
});
subject.next(2);
subject.next(3);
With the following output on the console:
observerA: 2
observerB: 2
observerA: 3
observerB: 3
subject.next(3);
I just created a project which explain what is the difference between all subjects:
https://github.com/piecioshka/rxjs-subject-vs-behavior-vs-replay-vs-async
https://i.stack.imgur.com/VyiFf.png
BehaviorSubject
keeps in memory the last value that was emitted by the observable. A regular Subject
doesn't.
BehaviorSubject
is like ReplaySubject
with a buffer size of 1.
UPDATE: There are edge use cases that distinguish those two. https://medium.com/javascript-everyday/behaviorsubject-vs-replaysubject-1-beware-of-edge-cases-b361153d9ccf
TLDR: If you want to provide an initial value at subscription time, even if nothing has been pushed to a Subject so far, use the BehaviorSubject. If you want to have the last value replayed to an observer, even if a Subject is already closed, use the ReplaySubject(1).
It might help you to understand.
import * as Rx from 'rxjs';
const subject1 = new Rx.Subject();
subject1.next(1);
subject1.subscribe(x => console.log(x)); // will print nothing -> because we subscribed after the emission and it does not hold the value.
const subject2 = new Rx.Subject();
subject2.subscribe(x => console.log(x)); // print 1 -> because the emission happend after the subscription.
subject2.next(1);
const behavSubject1 = new Rx.BehaviorSubject(1);
behavSubject1.next(2);
behavSubject1.subscribe(x => console.log(x)); // print 2 -> because it holds the value.
const behavSubject2 = new Rx.BehaviorSubject(1);
behavSubject2.subscribe(x => console.log('val:', x)); // print 1 -> default value
behavSubject2.next(2) // just because of next emission will print 2
A BehaviorSubject
holds one value (so we actually need to initialize a default value). When it is subscribed it emits that value immediately. A Subject
on the other hand, does not hold a value.
That actually means that in Subject
, the subscribers will only receive the upcoming value where as in BehaviorSubject
the subscribers will receive the previous value and also upcoming value.
More about the difference between BehaviorSubject
and Subject
can be found here
So, let's take an example to see how this will behave:
let mySubject = new Subject<number>();
mySubject.subscribe(x => console.log("The first Subscription : " + x));
mySubject.next(1);
mySubject.next(2);
mySubject.subscribe(x => console.log("The second Subscription : " + x));
mySubject.next(3);
// The first Subscription : 1
// The first Subscription : 2
// The first Subscription : 3
// The second Subscription : 3
Like we saw above, the first 2 values were output from the subject before the second subscription registered, so it didn't get them, it only got the new values after subscribed. The first subscription got them all, since it subscribed before the first values were output.
Now, let's change the subject
to BehaviorSubject
and see the difference:
let mySubject = new BehaviorSubject<number>(0);
mySubject.subscribe((x) => console.log('The first Subscription : ' + x));
mySubject.next(1);
mySubject.next(2);
mySubject.subscribe((x) => console.log('The second Subscription : ' + x));
mySubject.next(3);
// The first Subscription : 0 (since it's the initial value)
// The first Subscription : 1
// The first Subscription : 2
// The second Subscription : 2 (since it's the initial value for the seconde subscriber)
// The first Subscription : 3
// The second Subscription : 3
Now, notice how the first subscriber outputs 0
since the BehaviorSubject
was initialized with 0
. When the second subscriber subscribes, it immediately emits the '2' value since it was the last value to be handled so it acts as the initial value for it.
BehaviorSubject keeps in memory the last value that was emitted by the observable. A regular Subject doesn't. So we can update dynamic titles based on Behaviour Subject.
var bSubject= new Rx.BehaviorSubject(0); // 0 is the initial value
bSubject.subscribe({
next: (v) => console.log('observerA: ' + v) // output initial value, then new values on `next` triggers
});
bSubject.next(1); // output new value 1 for 'observer A'
bSubject.next(2); // output new value 2 for 'observer A', current value 2 for 'Observer B' on subscription
bSubject.subscribe({
next: (v) => console.log('observerB: ' + v) // output current value 2, then new values on `next` triggers
});
bSubject.next(3);
- With Output
A BehaviorSubject emits a value after subscription, a Subject no.
// Subject
const mySubject = new Rx.Subject().subscribe((v) => console.log(v)); // will return nothing
// BehaviorSubject
const myBehaviorSubject = new Rx.BehaviorSubject(666).subscribe((v) => console.log(v)); // will return 666 when subscription occurs
Success story sharing