take(1) vs firstValueFrom vs observable.value
Asked Answered
C

1

7

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);
Corse answered 11/4, 2022 at 6:37 Comment(2)
Regarding operators, the classic one would be first vs take(1), see https://mcmap.net/q/115854/-take-1-vs-firstLatishalatitude
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.Secretion
C
15

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$);

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

Cosgrove answered 11/4, 2022 at 7:37 Comment(4)
Thanks a lot, this is a great answer.Corse
@marlin whats the point of using take(1) inside the firstValueFrom method, when you already get the first value while using it.Bahena
I think you're right, it should not be necessaryCosgrove
@Cosgrove If it is not necessary why are you not editing your example to const value = await firstValueFrom(myStore$);Selfdevotion

© 2022 - 2024 — McMap. All rights reserved.