434

I'm not clear on the difference between a Subject and a BehaviorSubject. Is it just that a BehaviorSubject has the getValue() function?

Ran Turner
  • 14,906
  • 5
  • 47
  • 53
Mike Jerred
  • 9,551
  • 5
  • 22
  • 42

10 Answers10

548

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 published values.
P.M
  • 2,880
  • 3
  • 43
  • 53
ZahiC
  • 13,567
  • 3
  • 25
  • 27
  • 39
    So 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
  • 11
    @eric for Subject, yes. That is the distinction. – onefootswill Jul 06 '18 at 23:46
  • 15
    Note 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
  • 6
    If it helps: Subjects = Event - BehaviorSubject = State; – Jonathan Stellwag May 11 '20 at 11:37
  • 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
  • I think the answer below has a better example. https://stackoverflow.com/a/59784108/12097072 – stoneshishang Sep 21 '22 at 16:20
352

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
Sergey
  • 1,608
  • 1
  • 27
  • 40
Mohammed Safeer
  • 20,751
  • 8
  • 75
  • 78
189

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

enter image description here

piecioshka
  • 4,752
  • 2
  • 20
  • 29
28

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.

What it actually means is 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.

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.

More about the difference between BehaviorSubject and Subject can be found here

Ran Turner
  • 14,906
  • 5
  • 47
  • 53
26

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).

Moshe Yamini
  • 608
  • 9
  • 13
18

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 
Sanjeet kumar
  • 3,333
  • 3
  • 17
  • 26
2

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
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
    
    
pradeep
  • 3
  • 3
ANKIT MISHRA
  • 558
  • 4
  • 13
1

All three are quite different, let me give few more samples here,

const subject = new Rx.Subject();
const behaviorSubject = new Rx.BehaviorSubject([]);
const relaySubject = new Rx.ReplaySubject();


subject.next(1)

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

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

subject.subscribe(val => console.log('From Subject', val)); // this will not emits
behaviorSubject.subscribe(val => console.log('From BehaviorSubject', val)); // this will emits only last value
relaySubject.subscribe(val => console.log('From ReplaySubject', val)); // this will emit all values

Output screenshot here

As you can see, when we subscribe to the subject after we emit(i.e next(...)),

  1. subject - this will not get fired at all
  2. behaviorSubject - this will fire once with last value
  3. ReplaySubject - this will fire 3 time, as many next() we have

So the difference is mainly where you subscribe, whether it's before next() or after next().

In practical case, we fire event's only after next() [i.e after we fill data to the subjects].

sachinkondana
  • 564
  • 5
  • 15
1

Program to test all 4 Subjects types: Subject, BehaviorSubject, ReplaySubject and AsyncSubject

// 1. Subject - only value after subscribed
var subject = new Subject();
subject.next(1);
subject.next(2);
subject.complete();
subject.subscribe(
  (data) => this.log("Subject="+data),
  (error) => this.log(error),
  () => this.log('Complete Subject')
);
subject.next(3);
subject.next(4);

// 2. BehaviorSubject - only last value before subscribed and all after subscription
// calls on initalization, mandatory to specify a value
var subjectb = new BehaviorSubject<any>(5);
subjectb.next(1);
subjectb.next(2);
subjectb.complete();
subjectb.subscribe(
  (data) => this.log("Behavior="+data),
  (error) => this.log(error),
  () => this.log('Complete Behavior')
);

// 3. ReplaySubject - all specified last values before subscribed and all after subscription
// Does not call on initalization, no default value
var subjectr = new ReplaySubject(5);
subjectr.next(1);
subjectr.next(2);
subjectr.complete();
subjectr.subscribe(
  (data) => this.log("Replay="+data),
  (error) => this.log(error),
  () => this.log('Complete Replay')
);

// 4. AsyncSubject - only last values before calling complete
var subjecta = new AsyncSubject();
subjecta.next(1);
subjecta.next(2);
subjecta.complete();
subjecta.subscribe(
  (data) => this.log("Async="+data),
  (error) => this.log(error),
  () => this.log('Complete Async')
);

https://stackblitz.com/edit/example-rxjs-subject-e8vj9y?embed=1&file=app/app.component.ts

Sameer Jadhav
  • 271
  • 4
  • 10