I'm not clear on the difference between a Subject and a BehaviorSubject. Is it just that a BehaviorSubject has the getValue() function?
- 8,973
- 3
- 23
- 37
- 8,354
- 4
- 23
- 37
-
nice answer: https://stackoverflow.com/a/55991072/7186739 – dev Dec 29 '21 at 07:23
-
@dev I agree... – William May 19 '22 at 07:02
8 Answers
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:
BehaviorSubjectshould be created with an initial value: newRx.BehaviorSubject(1)- Consider
ReplaySubjectif you want the subject to get previously publised values.
-
31So do you mean you have to subscribe to subject before subject.next() to for this to work? – Eric Huang Jun 28 '18 at 03:02
-
10
-
15Note that you have to pass in the first value to BehaviorSubject's constructor ;) – mrmashal Sep 23 '18 at 08:02
-
if we create subject with boolean even subject emits rite?? const subject = new Subject
(); subject.next(true); – user2900572 Feb 26 '20 at 07:20 -
3
-
When you say "A Subject doesn't hold a value" it's kind of confusing. If you show one more example for "Subject" where you subscribe first and _then_ call `.next(1)`, it will make your answer a lot more clear. – Sergey Jun 05 '20 at 17:14
-
Often, we do not have the initial value, since we would like to [convert an observable to behavior subject](https://stackoverflow.com/a/58199823/5770014): don't worry about the initial value, just use some 'empty' value. – minus one Dec 13 '21 at 09:14
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
- 1,529
- 1
- 25
- 38
- 19,163
- 8
- 72
- 77
-
29Its also more correct : "BehaviourSubject will return the initial value or the current value on Subscription" is a better explanation than "A BehaviorSubject holds one value." – Davy Jun 22 '18 at 13:28
-
1I put the code above on Stackblitz: https://stackblitz.com/edit/rxjs-subjectvsbehaviorsubject – Fredrik_Borgstrom Oct 26 '18 at 22:22
-
-
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
- 3,945
- 2
- 19
- 29
-
2
-
1Wow, I wish the official documentation would be that simple and helpful, thanks! – Tom Mar 25 '22 at 10:46
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).
- 463
- 6
- 10
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
- 3,212
- 3
- 14
- 21
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.
- 8,973
- 3
- 23
- 37
-
1@Rad thank you for you explanation -> it really made sense to me – Ursus Schneider Mar 17 '22 at 09:39
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
- 3
- 3
- 399
- 2
- 11
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
- 1
- 2