RxJS Subject veya Observable'ın mevcut değerini nasıl alabilirim?


210

Bir Açısal 2 hizmetim var:

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

Her şey harika çalışıyor. Ama abone olmama gerek olmayan başka bir bileşenim var, sadece isLoggedIn'in şu andaki değerini belirli bir zamanda alması gerekiyor. Bunu nasıl yapabilirim?

Yanıtlar:


347

A Subjectveya Observablegeçerli bir değeri yok. Bir değer yayıldığında, abonelere iletilir veObservable yapılır.

Mevcut bir değere sahip olmak istiyorsanız, BehaviorSubjecttam olarak bu amaç için tasarlanmış olanı kullanın .BehaviorSubjectson yayınlanan değeri tutar ve hemen yeni abonelere gönderir.

Ayrıca getValue()geçerli değeri elde etmek için bir yöntemi vardır .


1
Merhaba, bu benim kötü, onlar şimdi rxjs 5 aynı şey. Yukarıdaki kaynak kodu bağlantı rxjs4 atıfta bulundu.
schrodinger'ın kodu

91
RxJS 5'in yazarından önemli not: getValue()Yanlış bir şey yaptığınız BÜYÜK bir kırmızı bayrak. Orada kaçış kapağı olarak var. Genellikle RxJS ile yaptığınız her şey açıklayıcı olmalıdır. getValue()zorunludur. Kullanıyorsanız getValue(), yanlış veya garip bir şey yapma olasılığınızın% 99,9'u vardır.
Ben Lesh

1
@BenLesh Ya bir BehaviorSubject<boolean>(false)geçiş yapmam ve beğenmem gerekirse ?
bob

3
@BenLesh getValue (), onClick-> dispatchToServer (x.getValue ()) gibi anlık bir işlem yaparak söylemek için çok yararlıdır, ancak gözlemlenebilir zincirlerde kullanmayın.
FlavorScape

2
@ IngoBürk Önerinizi gerekçelendirebilmemin tek yolu, foo$bunun bir BehaviorSubject(yani Observableertelenmiş bir kaynaktan sıcak veya soğuk olarak tanımlanmış ve belki de sıcak veya soğuk olarak tanımlanmış) olduğunu bilmemenizdir . Ancak daha sonra kullanılmak üzere devam beri .nextüzerinde $foouygulamanız o dayanır demektir kendisi olmanın bir BehaviorSubjectyani sadece kullanmamak için hiçbir gerekçe yoktur .valueilk etapta geçerli değerini alır.
Simon_Weaver

146

Yapmanız gereken tek yol "dışında" değerlerini elde edilmez bir gözlemlenebilir / Konu abone beraber!

Eğer kullanıyorsanız getValue(), bildirim paradigmasında zorunlu bir şey yapıyorsunuz. Orada bir kaçış kapağı var, ama kullanmamalısınız zamanın% 99.9'u getValue(). Birkaç ilginç şey vargetValue()Yapacak : Özne aboneliği kaldırılmışsa bir hata atar, özne öldüğü için bir değer almanızı önler, çünkü hatalı, vb. Ama yine de, bir kaçış olarak orada nadir durumlar için ambar.

Bir Konudan veya Gözlemlenebilirden en son değeri bir "Rx-y" yoluyla elde etmenin birkaç yolu vardır:

  1. Kullanmak BehaviorSubject: Ama aslında abone . İlk abone olduğunuzdaBehaviorSubject olduğunuzda, eşzamanlı olarak aldığı veya başlatıldığı önceki değeri gönderir.
  2. A kullanarak ReplaySubject(N): Bu önbelleklenirN Değerleri ve yeni abonelere yeniden oynatır.
  3. A.withLatestFrom(B): BGözlenebilir Ayayıldığında gözlemlenebilirden en son değeri almak için bu operatörü kullanın . Dizideki her iki değeri de verir [a, b].
  4. A.combineLatest(B): Yalnızca bu operatör en son değerleri elde etmek Ave Bher zaman ya Aya Byayar. Dizideki her iki değeri de verir.
  5. shareReplay(): A ile Gözlemlenebilir çok noktaya yayın yapar ReplaySubject, ancak hata durumunda gözlemlenebilir olanı yeniden denemenize olanak tanır. (Temel olarak size söz veren önbellekleme davranışı verir).
  6. publishReplay(), publishBehavior(initialValue), multicast(subject: BehaviorSubject | ReplaySubject), Vs: Diğer operatörler bu kaldıraç BehaviorSubjectve ReplaySubject. Aynı şeyin farklı tatları, temelde tüm bildirimleri bir konu aracılığıyla hunlayarak gözlemlenebilir kaynağı çok noktaya yayınlar. connect()Konuyla kaynağa abone olmak için aramanız gerekir .

19
getValue () kullanmanın neden kırmızı bayrak olduğunu öğrenebilir misiniz? Kullanıcı bir düğmeyi tıklattığında gözlemlenebilir olan geçerli değere yalnızca bir kez ihtiyacım olursa ne olur? Değere abone olmalı mıyım ve hemen abonelikten çıkmalı mıyım?
Alev

5
Bazen sorun değil. Ancak çoğu zaman, kodun yazarının, olmaması gereken çok zorunlu bir şey (genellikle yan etkilerle) yaptığını gösteren bir işarettir. Sen yapabilirsin click$.mergeMap(() => behaviorSubject.take(1))yanı sorunu çözmek için.
Ben Lesh

1
Harika bir açıklama! Aslında, Gözlemlenebilir'i tutabilir ve yöntemleri kullanabilirseniz, çok daha iyi bir yoldur. Gözlemlenebilir her yerde kullanılan ancak BehaviourSubject olarak tanımlanmış yalnızca birkaçı bağlamında, daha az tutarlı bir kod olacaktır. Önerdiğiniz yöntemler, Gözlemlenebilir türleri her yerde tutmamı sağladı.
JLavoie

2
getValue () yapmak çok kullanışlı, sadece geçerli değeri (tıklama üzerine sunucuya göndermek gibi) göndermek istiyorsanız iyi. ancak gözlemlenebilir operatörleri zincirlerken kullanmayın.
FlavorScape

1
Ben bir AuthenticationServicekullanılan BehaviourSubjectgeçerli oturum açma durumunu ( boolean trueveya false) saklamak için kullanır . isLoggedIn$Devletin ne zaman değiştiğini bilmek isteyen aboneler için gözlemlenebilir bir durum ortaya koymaktadır. Ayrıca , temel alınan öğeyi get isLoggedIn()çağırarak oturum açmış olan geçerli durumu döndüren bir özellik ortaya çıkarır - bu, kimlik doğrulama görevlisi tarafından geçerli durumu denetlemek için kullanılır. Bu benim için mantıklı bir kullanım gibi görünüyor ...? getValue()BehaviourSubjectgetValue()
Dan King

13

Geç gelen abonelerin değeri geldikten sonra Konuya abone oldukları benzer bir durum yaşadım.

BehaviorSubject benzer ReplaySubject bu durumda bir cazibe gibi çalışır bulundu . Ve işte daha iyi bir açıklama linki: http://reactivex.io/rxjs/manual/overview.html#replaysubject


1
Angular4 uygulamamda bana yardımcı oldu - Aboneliği bileşen oluşturucusundan ngOnInit'e () (böyle bir bileşen güzergahlar arasında paylaşılıyor) taşımak zorunda kaldım, birisinin benzer bir sorunu olması durumunda buradan ayrıldım
Luca

1
Bir API'den değerler almak ve farklı bileşenlerde değişkenler ayarlamak için bir hizmet kullandığım Angular 5 uygulamasıyla ilgili bir sorunum vardı. Özneleri / gözlemlenebilirleri kullanıyordum, ancak bir rota değişikliğinden sonra değerleri zorlayamazdı. ReplaySubject, Subject'nin yerine bir damla oldu ve her şeyi çözdü.
jafaircl

1
ReplaySubject (1) kullandığınızdan emin olun, aksi takdirde yeni aboneler daha önce yayınlanan her değeri sırayla alacaktır - bu çalışma zamanında her zaman belirgin değildir
Drenai

@Drenai anladığım kadarıyla ReplaySubject (1) BehaviorSubject () ile aynı şekilde davranır
Kfir Erez

Tam olarak aynı değil, büyük fark ReplaySubject'in next()henüz işlevi çağrılmadıysa abone olduğunda hemen varsayılan bir değer yaymaması, BehaviourSubject ise. Anında yayma, örneğin bir hizmette gözlemlenebilir veri kaynağı olarak
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
// .................
// .................
// .................

Nasıl uygulanacağı ile ilgili makalenin tamamını buradan kontrol edebilirsiniz. https://www.imkrish.com/how-to-get-current-value-of-observable-in-a-clean-way/


3

Başlangıçta Öznenin geçerli değerine sahip olması ve ardından değişiklikleri dinlemek için Özneye abone olması gereken alt bileşenlerde de aynı sorunla karşılaştım. Bileşenlerin, örneğin:

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;
  }
}

Geçerli değere ihtiyaç duyan bir bileşen, bundan sonra hizmetten erişebilir, yani:

sessionStorage.isLoggedIn

Bunun doğru uygulama olup olmadığından emin değilim :)


2
Bileşen görünümünüzde gözlemlenebilir bir değere ihtiyacınız varsa, asyncboruyu kullanabilirsiniz .
Ingo Bürk

2

Benzer görünümlü bir cevap indirildi. Ancak, sınırlı durumlar için burada önerdiğimi haklı çıkarabileceğimi düşünüyorum.


Bir gözlemlenebilir öğenin geçerli bir değeri olmadığı doğru olsa da , çoğu zaman hemen kullanılabilir bir değere sahip olacaktır . Örneğin redux / flux / akita mağazalarında, bir dizi gözlemlenebilir veriye dayalı olarak merkezi bir mağazadan veri talep edebilirsiniz ve bu değer genellikle hemen kullanılabilir olacaktır.

Eğer durum buysa, o zaman subscribedeğer hemen geri gelecektir.

Diyelim ki bir hizmet çağrınız var ve tamamlandığında mağazanızdan potansiyel olarak yaymayabilecek bir şeyin en son değerini almak istiyorsunuz :

Bunu yapmaya çalışabilirsiniz (ve işleri mümkün olduğunca 'boruların içinde' tutmalısınız):

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

                        // we have serviceCallResponse and customer 
                     });

Buradaki sorun, ikincil gözlemlenebilir bir değer yayana kadar bloke olacağıdır, bu potansiyel olarak asla olamaz.

Kendimi son zamanlarda gözlemlenebilir bir değerlendirmeyi sadece bir değer mevcut olduğunda değerlendirmeye ihtiyaç duyduğumu gördüm ve daha da önemlisi, olmadığını tespit edebilmem gerekiyordu. Sonunda bunu yaptım:

 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');
                        }
                     });

Yukarıdakilerin tümü subscribeiçin değeri almak için kullanıyorum (@Ben tartıştığı gibi). Ben bir .valueözellik olmasa bile BehaviorSubject.


1
Btw bu çalışır çünkü varsayılan olarak 'zamanlama' geçerli iş parçacığı kullanır.
Simon_Weaver

1

Kulağa aşırı gelebilir gibi gelse de, bu sadece Gözlemlenebilir tutmak için başka bir "olası" çözümdür tipte ve kazan plakasını azaltmak ...

Bir Gözlemlenebilir öğesinin geçerli değerini almak için her zaman bir uzantı alıcısı oluşturabilirsiniz .

Bunu yapmak için, Observable<T>arabirimi bir global.d.tsbildirimler bildirim dosyasında genişletmeniz gerekir . Ardından, uzantıobservable.extension.ts alıcıyı bir dosyaya uygulayın ve son olarak uygulamanıza hem yazımları hem de uzantı dosyasını ekleyin.

Uzantıları Açısal uygulamanıza nasıl ekleyeceğinizi öğrenmek için bu StackOverflow Yanıtına başvurabilirsiniz .

// 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

En son yayınlanan değeri Gözlemlenebilir'den ayrı olarak saklayabilirsiniz. Sonra gerektiğinde okuyun.

let lastValue: number;

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

1
Bazı şeyleri gözlemlenebilirlerin dışında saklamak reaktif bir yaklaşım değildir. Bunun yerine, gözlemlenebilir akışların içinden akan olabildiğince fazla veriye sahip olmalısınız.
ganqqwerty

0

Bunu yapmanın en iyi yolu kullanmaktır Behaviur Subject, işte bir örnek:

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

Bir abonelik oluşturulabilir ve yayılan ilk öğe kaldırıldıktan sonra. Boru, giriş olarak Gözlemlenebilir kullanan bir işlevdir ve ilk gözlemlenebilirliği değiştirmeden başka bir Gözlemlenebilir çıkış döndürür. Açısal 8.1.0. Paketler: "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);
      });

    }
  }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.