3

In my project, I use BehaviorSubjects as data stores to store the state of my application.

I need only the current value that is currently held in the BehaviorSubject store, and don't need to subscribe to future values that can be emitted through the behavior subject.

I found few implementations of how to do this: using pipe(take(1)), firstValueFrom and .value.

Do all work the same way and read the current value in the BehaviorSubject store? What are the differences between them, if any?

private myStore$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

// reading only one current value using take(1):
this.myStore$.pipe(take(1))
    .subscribe(value => console.log('current value = ', value));

// reading only one current value using firstValueFrom:
const value = await firstValueFrom(this.myStore$);
console.log('current value = ', value);

// reading only one current value using this.myStore$.value:
const value = this.myStore$.value;
console.log('current value = ', value);
Lorraine R.
  • 1,545
  • 1
  • 14
  • 39
  • Regarding operators, the classic one would be `first` vs `take(1)`, see https://stackoverflow.com/questions/42345969/take1-vs-first – Aldin Bradaric Apr 11 '22 at 06:42
  • Well, one of the differences is that `myStore$.value` is synchronous, you don't need any async constructs to access it. The `take(1)` variant is asynchronous but it is still using observables (recommended approach in my opinion if you want to go with async access for this value. Check Aldin's comment about `take(1)` vs `first()` and see which one best fits your requirements) and the `firstValueFrom` will give you a promise object that needs to be handled using either `await` like in your snippet, or using `then`. – Octavian Mărculescu Apr 11 '22 at 06:44

1 Answers1

11

Basically using take(1) or firstValueFrom is the same because they access the value asynchronously:

myStore$.pipe(
  take(1)
).subscribe(value => {...});

firstValueFrom turns Observable into a Promise so you can use async/await:

const value = await firstValueFrom(myStore$.pipe(
  take(1)
));

Using BehaviorSubject.value to synchronously access its value is in general not recommended. It might behave unexpectedly if BehaviorSubject errors or when it's unsubscribed:

import { BehaviorSubject } from 'rxjs';

const subject1 = new BehaviorSubject(1);
console.log(subject1.value);
subject1.error(new Error('broken'));
try {
  console.log(subject1.value);
} catch (e) {
  console.error(e);
}

const subject2 = new BehaviorSubject(2);
console.log(subject2.value);
subject2.unsubscribe();
try {
  console.log(subject2.value);
} catch (e) {
  console.error(e);
}

Live demo: https://stackblitz.com/edit/rxjs-npalak?devtoolsheight=60

martin
  • 93,354
  • 25
  • 191
  • 226