RxJS Subject 또는 Observable의 현재 가치를 얻는 방법?


206

Angular 2 서비스가 있습니다.

import {Storage} from './storage';
import {Injectable} from 'angular2/core';
import {Subject}    from 'rxjs/Subject';

@Injectable()
export class SessionStorage extends Storage {
  private _isLoggedInSource = new Subject<boolean>();
  isLoggedIn = this._isLoggedInSource.asObservable();
  constructor() {
    super('session');
  }
  setIsLoggedIn(value: boolean) {
    this.setItem('_isLoggedIn', value, () => {
      this._isLoggedInSource.next(value);
    });
  }
}

모든 것이 잘 작동합니다. 그러나 구독 할 필요가없는 다른 구성 요소가 있으며 특정 시점에서 isLoggedIn의 현재 값을 가져와야합니다. 어떻게해야합니까?

답변:


341

A Subject또는 Observable현재 값이 없습니다. 값이 방출되면 구독자에게 전달되고 값 Observable이 완료됩니다.

현재 값을 원한다면 BehaviorSubject정확히 그 목적을 위해 설계된 값을 사용하십시오 . BehaviorSubject마지막으로 방출 된 값을 유지하고 새로운 가입자에게 즉시 방출합니다.

getValue()현재 값을 얻는 방법도 있습니다 .


1
안녕, 그것은 나쁘다, 그들은 지금 rxjs 5에서 같은 것입니다. 위의 소스 코드 링크는 rxjs4를 참조하고있었습니다.
schrodinger의 코드

84
RxJS 5 작성자의 중요 참고 사항 : Using getValue()is a huge 거대한 깃발입니다. 탈출 해치가 있습니다. 일반적으로 RxJS로하는 모든 것은 선언적이어야합니다. getValue()필수적입니다. 을 사용하는 경우 getValue()99.9 %의 확률로 잘못되었거나 이상한 일을 할 수 있습니다.
벤 레시

1
@ BenLesh 내가 BehaviorSubject<boolean>(false)있고 그것을 토글하고 싶다면 어떻게 해야합니까?
bob

3
@BenLesh getValue ()는 onClick-> dispatchToServer (x.getValue ())와 같은 즉각적인 작업을 수행하는 데 매우 유용하지만 관찰 가능한 체인에서는 사용하지 마십시오.
FlavorScape

2
IngoBürk는 유일한 방법 나는 당신이 알고하지 않은 경우 귀하의 제안은 정당화 할 수 @ foo$을했다 BehaviorSubject(즉, 그것은으로 정의 Observable하고 어쩌면 지연된 소스로부터의 뜨거운 또는 감기). 그러나 당신은 그 사용에 진학 이후 .next$foo구현 그것을에 의존하는 것을 의미 자체 A가 BehaviorSubject그래서 그냥 사용하지에 대한 정당성이 없다 .value처음에 현재 값을 얻을 수 있습니다.
Simon_Weaver 3

145

당신이 유일한 방법 해야한다 "에서"값을 얻지는 관찰 가능한 / 주제 구독 함께!

당신이 사용 getValue()하고 있다면 선언적 패러다임에서 명령적인 일을하고있는 것입니다. 탈출구로 있지만 99.9 %는 사용하지 않아야합니다 getValue(). 몇 가지 흥미로운 일 getValue()이 있습니다. 주체가 구독을 취소하면 오류가 발생하고 오류로 인해 주제가 죽은 경우 값을 얻지 못하게됩니다. 그러나 다시, 탈출로 드문 상황에 부화.

"Rx-y"방식으로 Subject 또는 Observable에서 최신 값을 얻는 방법에는 여러 가지가 있습니다.

  1. 사용 BehaviorSubject: 그러나 실제로 구독 . 처음 구독 BehaviorSubject하면 수신하거나 초기화 한 이전 값을 동 기적으로 전송합니다.
  2. Using a ReplaySubject(N): N값 을 캐시 하고 새 구독자에게 재생합니다.
  3. A.withLatestFrom(B):이 연산자를 사용하여 관찰 B가능 A방출 시 관찰 가능 값에서 최신 값을 가져옵니다 . 배열의 두 값을 모두 제공합니다 [a, b].
  4. A.combineLatest(B): 사용이 연산자는 가장 최근의 값을 얻을 A하고 B때마다 하나 A또는 B방출한다. 배열의 두 값을 모두 제공합니다.
  5. shareReplay():를 통해 Observable 멀티 캐스트를 작성 ReplaySubject하지만 오류가 발생하면 Observable을 재 시도 할 수 있습니다. 기본적으로 캐쉬 동작을 약속합니다.
  6. publishReplay(), publishBehavior(initialValue), multicast(subject: BehaviorSubject | ReplaySubject), 등 : 다른 사업자를 활용 BehaviorSubject하고 ReplaySubject. 같은 것의 다른 풍미, 그들은 기본적으로 주제를 통해 모든 알림을 깔아서 관찰 가능한 소스를 멀티 캐스트합니다. connect()제목으로 소스를 구독 하려면 전화해야합니다 .

19
getValue () 사용이 적기 인 이유를 자세히 설명 할 수 있습니까? 옵저버 블의 현재 값이 한 번만 필요한 경우 사용자가 버튼을 클릭하는 순간은 어떻게됩니까? 값을 구독하고 즉시 구독을 취소해야합니까?
Flame

5
때때로 괜찮습니다. 그러나 종종 코드 작성자가하지 말아야 할 곳에서 매우 중요한 일을합니다 (보통 부작용이 있음). 당신은 click$.mergeMap(() => behaviorSubject.take(1))또한 문제를 해결하기 위해 할 수 있습니다 .
Ben Lesh

1
좋은 설명! 실제로 Observable을 유지하고 메소드를 사용할 수 있다면 훨씬 더 좋은 방법입니다. Observable이있는 typescript의 컨텍스트에서 BehaviourSubject로 정의 된 몇 가지만 사용되는 경우 코드가 덜 구성됩니다. 제안한 방법으로 Observable 유형을 모든 곳에서 유지할 수있었습니다.
JLavoie

2
클릭하면 서버에 현재 값을 보내는 것과 같이 현재 값을 전달하려는 경우 getValue ()를 수행하는 것이 매우 편리합니다. 관측 가능한 연산자를 체인으로 연결할 때는 사용하지 마십시오.
FlavorScape

1
I는이 AuthenticationService사용되는 BehaviourSubject전류를 저장하는 상태 (기록 boolean true또는 false). 그것은 노출 isLoggedIn$상태가 변경 될 때 알고 싶은 가입자 관찰. 또한 기본 get isLoggedIn()을 호출 getValue()하여 로그인 된 현재 상태를 반환 하는 속성 을 노출합니다 . 이는 BehaviourSubject내 인증 가드가 현재 상태를 확인하는 데 사용합니다. 이것은 getValue()나에게 현명한 사용처럼 보인다 ...?
Dan King

12

나는 늦은 구독자가 그 가치에 도달 한 후 주제를 구독하는 비슷한 상황이 있었다.

이 경우 BehaviorSubject와 비슷한 ReplaySubject 가 매력처럼 작동 한다는 것을 알았 습니다 . 더 나은 설명으로 연결되는 링크는 다음과 같습니다. http://reactivex.io/rxjs/manual/overview.html#replaysubject


1
그건 내 Angular4 응용 프로그램에서 저를 도왔다 - 나는 경우를 누군가 여기에 남겨두고 ngOnInit () (예 : 구성 요소가 경로를 통해 공유)에 구성 요소 생성자에서 구독을 이동했다가 유사한 문제
루카

1
서비스를 사용하여 API에서 값을 가져오고 다른 구성 요소에서 변수를 설정하는 Angular 5 앱에 문제가 있습니다. 주제 / 관측 가능 항목을 사용하고 있었지만 경로 변경 후 값을 푸시하지 않습니다. ReplaySubject는 Subject의 대체품으로 모든 것을 해결했습니다.
jafaircl

1
ReplaySubject (1)를 사용하고 있는지 확인하십시오. 그렇지 않으면 새 가입자가 이전에 방출 된 모든 값을 순서대로 가져옵니다. 이는 런타임시 항상 명확하지는 않습니다.
Drenai

@ Drenai 내가 이해하는 한 ReplaySubject (1)는 BehaviorSubject ()와 동일하게 작동합니다
Kfir Erez

똑같지는 않지만, BehaviourSubject는 next()함수가 아직 호출되지 않은 경우 구독 할 때 ReplaySubject가 즉시 기본값을 방출하지 않는다는 큰 차이점 이 있습니다. 즉시 방출은 BehaviourSubject가 서비스에서 관찰 가능한 데이터 소스로 사용될
때보기에

5
const observable = of('response')

function hasValue(value: any) {
  return value !== null && value !== undefined;
}

function getValue<T>(observable: Observable<T>): Promise<T> {
  return observable
    .pipe(
      filter(hasValue),
      first()
    )
    .toPromise();
}

const result = await getValue(observable)
// Do the logic with the result
// .................
// .................
// .................

여기에서 구현 방법에 대한 전체 기사를 확인할 수 있습니다. https://www.imkrish.com/how-to-get-current-value-of-observable-in-a-clean-way/


3

하위 구성 요소에서 처음에 주제의 현재 값을 가져야하는 동일한 문제가 발생 한 다음 주제를 구독하여 변경 사항을 듣습니다. 구성 요소가 다음과 같이 액세스 할 수 있도록 서비스의 현재 값을 유지합니다.

import {Storage} from './storage';
import {Injectable} from 'angular2/core';
import {Subject}    from 'rxjs/Subject';

@Injectable()
export class SessionStorage extends Storage {

  isLoggedIn: boolean;

  private _isLoggedInSource = new Subject<boolean>();
  isLoggedIn = this._isLoggedInSource.asObservable();
  constructor() {
    super('session');
    this.currIsLoggedIn = false;
  }
  setIsLoggedIn(value: boolean) {
    this.setItem('_isLoggedIn', value, () => {
      this._isLoggedInSource.next(value);
    });
    this.isLoggedIn = value;
  }
}

현재 값이 필요한 구성 요소는 서비스에서 액세스 할 수 있습니다.

sessionStorage.isLoggedIn

이것이 올바른 습관인지 확실하지 않습니다 :)


2
구성 요소보기에서 관찰 가능 값이 필요한 경우 async파이프를 사용할 수 있습니다 .
Ingo Bürk

2

비슷한 찾고 대답을 downvoted했다. 그러나 제한된 경우에 대해 내가 제안하는 것을 정당화 할 수 있다고 생각합니다.


관찰 가능 항목에 현재 값 이없는 것이 사실이지만 , 종종 즉시 사용 가능한 값을 갖습니다. 예를 들어 redux / flux / akita 상점의 경우 여러 관측 가능 항목을 기반으로 중앙 상점에 데이터를 요청할 수 있으며 일반적으로 해당 값을 즉시 사용할 수 있습니다.

이 경우이면 subscribe값이 즉시 돌아옵니다.

그럼 당신이 서비스에 전화를했다 말, 및 완료에 당신이 상점에서 무언가의 최신 값을 싶어 할 가능성이 방출되지 않을 수도 있음 :

당신은 이것을 시도 할 수 있습니다 (그리고 가능한 한 많은 것을 파이프 안에 보관해야합니다) :

 serviceCallResponse$.pipe(withLatestFrom(store$.select(x => x.customer)))
                     .subscribe(([ serviceCallResponse, customer] => {

                        // we have serviceCallResponse and customer 
                     });

이것의 문제는 2 차 관측 가능 값이 방출 될 때까지 차단 될 수 있다는 것입니다.

나는 최근 에 값을 즉시 사용할 수있는 경우에만 관측 가능 항목을 평가해야한다는 것을 알았 으며, 더 중요하게는 그렇지 않은 경우를 감지 할 수 있어야했습니다. 나는 이것을 끝내었다.

 serviceCallResponse$.pipe()
                     .subscribe(serviceCallResponse => {

                        // immediately try to subscribe to get the 'available' value
                        // note: immediately unsubscribe afterward to 'cancel' if needed
                        let customer = undefined;

                        // whatever the secondary observable is
                        const secondary$ = store$.select(x => x.customer);

                        // subscribe to it, and assign to closure scope
                        sub = secondary$.pipe(take(1)).subscribe(_customer => customer = _customer);
                        sub.unsubscribe();

                        // if there's a delay or customer isn't available the value won't have been set before we get here
                        if (customer === undefined) 
                        {
                           // handle, or ignore as needed
                           return throwError('Customer was not immediately available');
                        }
                     });

위의 모든 subscribe것에서 나는 @Ben이 논의한 것처럼 값을 얻는 데 사용 하고 있습니다. .value내가있는 경우에도 속성을 사용하지 않습니다 BehaviorSubject.


1
Btw는 기본적으로 'schedule'이 현재 스레드를 사용하기 때문에 작동합니다.
Simon_Weaver

1

과잉으로 들릴 수도 있지만 이것은 Observable 유형 을 유지 하고 상용구를 줄이는 또 다른 "가능한"솔루션입니다 ...

Observable의 현재 값을 얻기 위해 항상 확장 getter 를 작성할 수 있습니다 .

이렇게하려면 타이핑 선언 파일 Observable<T>에서 인터페이스 를 확장해야 global.d.ts합니다. 그런 다음 파일에 확장 getter 를 구현 observable.extension.ts하고 마지막으로 입력과 확장 파일을 모두 애플리케이션에 포함하십시오.

StackOverflow 응답 을 참조 하여 확장을 Angular 응용 프로그램에 포함시키는 방법을 알 수 있습니다 .

// global.d.ts
declare module 'rxjs' {
  interface Observable<T> {
    /**
     * _Extension Method_ - Returns current value of an Observable.
     * Value is retrieved using _first()_ operator to avoid the need to unsubscribe.
     */
    value: Observable<T>;
  }
}

// observable.extension.ts
Object.defineProperty(Observable.prototype, 'value', {
  get <T>(this: Observable<T>): Observable<T> {
    return this.pipe(
      filter(value => value !== null && value !== undefined),
      first());
  },
});

// using the extension getter example
this.myObservable$.value
  .subscribe(value => {
    // whatever code you need...
  });

0

마지막으로 방출 된 값을 Observable과 별도로 저장할 수 있습니다. 그런 다음 필요할 때 읽으십시오.

let lastValue: number;

const subscription = new Service().start();
subscription
    .subscribe((data) => {
        lastValue = data;
    }
);

1
옵저버 블 외부에 일부 물건을 저장하는 반응적인 접근 방식이 아닙니다. 대신 관측 가능한 스트림 내부에 가능한 많은 데이터가 흐르도록해야합니다.
ganqqwerty

0

이를 수행하는 가장 좋은 방법은을 사용 Behaviur Subject하는 것입니다. 예는 다음과 같습니다.

var sub = new rxjs.BehaviorSubject([0, 1])
sub.next([2, 3])
setTimeout(() => {sub.next([4, 5])}, 1500)
sub.subscribe(a => console.log(a)) //2, 3 (current value) -> wait 2 sec -> 4, 5

0

구독을 생성 할 수 있으며 첫 번째 방출 된 항목을 삭제 한 후. 파이프는 Observable을 입력으로 사용하고 첫 번째 Observable을 수정하지 않고 다른 Observable을 출력으로 리턴하는 함수입니다. 각도 8.1.0. 패키지 : "rxjs": "6.5.3","rxjs-observable": "0.0.7"

  ngOnInit() {

    ...

    // If loading with previously saved value
    if (this.controlValue) {

      // Take says once you have 1, then close the subscription
      this.selectList.pipe(take(1)).subscribe(x => {
        let opt = x.find(y => y.value === this.controlValue);
        this.updateValue(opt);
      });

    }
  }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.