RxJS विषय या अवलोकन का वर्तमान मूल्य कैसे प्राप्त करें?


206

मेरे पास एक कोणीय 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 का उल्लेख कर रहा था।
स्कॉलर का कोड

84
RxJS 5 के लेखक का महत्वपूर्ण नोट: getValue()एक विशाल लाल ध्वज का उपयोग करना आप कुछ गलत कर रहे हैं। यह भागने की हैच के रूप में है। आम तौर पर RxJS के साथ आप जो कुछ भी करते हैं वह घोषणात्मक होना चाहिए। getValue()अनिवार्य है। यदि आप उपयोग कर रहे हैं getValue(), तो 99.9% संभावना है कि आप कुछ गलत या अजीब कर रहे हैं।
बेन लेश

1
@BenLesh क्या होगा अगर मेरे पास एक है BehaviorSubject<boolean>(false)और इसे टॉगल करना पसंद है?
बॉब

3
@BenLesh getValue () कहने के लिए बहुत उपयोगी है, तात्कालिक क्रिया करते हुए, जैसे onClick-> dispatchToServer (x.getValue ()) लेकिन इसका उपयोग अवलोकन योग्य श्रृंखलाओं में न करें।
फ्लेवर्डस्केप

2
@ IngoBürk आपके सुझाव को सही ठहराने का एकमात्र तरीका यह है कि यदि आप नहीं जानते कि foo$यह एक था BehaviorSubject(यानी इसे परिभाषित किया गया है Observableऔर शायद यह स्थगित स्रोत से गर्म या ठंडा है)। लेकिन जब से तुम तो उपयोग पर जाने .nextपर $fooही मतलब है कि आपके कार्यान्वयन यह पर निर्भर करता है किया जा रहा है एक BehaviorSubjectतो वहाँ बस का उपयोग नहीं करने के लिए कोई औचित्य है .valueपहली जगह में वर्तमान मूल्य प्राप्त करने के लिए।
साइमन_वेवर

144

एकमात्र तरीका आपको "ऑब्ज़र्वेबल / सब्जेक्ट" से मान प्राप्त करना चाहिए "" सदस्यता के साथ है!

यदि आप उपयोग getValue()कर रहे हैं तो आप घोषणात्मक प्रतिमान में कुछ जरूरी कर रहे हैं। यह एक भागने की हैच के रूप में है, लेकिन उस समय का ९९.९% जो आपको उपयोग नहीं करना चाहिए getValue()कुछ दिलचस्प चीजें हैं जो getValue()यह करेंगी: यह एक त्रुटि को फेंक देगा यदि विषय को सदस्यता समाप्त कर दिया गया है, तो यह आपको एक मूल्य प्राप्त करने से रोकेगा यदि विषय मर चुका है क्योंकि यह गलत है, आदि, लेकिन फिर, यह एक भागने के रूप में है दुर्लभ परिस्थितियों के लिए हैच।

"Rx-y" तरीके में विषय या अवलोकन से नवीनतम मान प्राप्त करने के कई तरीके हैं:

  1. उपयोग करना BehaviorSubject: लेकिन वास्तव में इसकी सदस्यता लेना । जब आप पहली बार इसकी सदस्यता लेते हैं, BehaviorSubjectतो इसे प्राप्त पिछले मूल्य को सिंक्रोनाइज़ किया जाएगा या इसके साथ आरंभ किया गया था।
  2. A का उपयोग करना ReplaySubject(N): यह Nमानों को कैश करेगा और उन्हें नए ग्राहकों को फिर से देगा ।
  3. A.withLatestFrom(B): Bअवलोकनीय Aउत्सर्जन होने पर सबसे हाल का मान प्राप्त करने के लिए इस ऑपरेटर का उपयोग करें । आपको एक सरणी में दोनों मान देगा [a, b]
  4. A.combineLatest(B): इस ऑपरेटर को सबसे हाल के मूल्यों से Aऔर Bहर बार Aया Bउत्सर्जन करने के लिए उपयोग करें । आपको एक सरणी में दोनों मान देगा।
  5. shareReplay(): अ ऑब्जर्वेबल मल्टिकास्ट बनाता है एक के माध्यम से ReplaySubject, लेकिन आप अवलोकनीय को त्रुटि पर पुन: प्रयास करने की अनुमति देता है। (मूल रूप से यह आपको वह वादा-वाई कैशिंग व्यवहार देता है)।
  6. publishReplay(), publishBehavior(initialValue), multicast(subject: BehaviorSubject | ReplaySubject), आदि: अन्य ऑपरेटरों कि लाभ उठाने BehaviorSubjectऔर ReplaySubject। एक ही चीज़ के विभिन्न स्वाद, वे मूल रूप से किसी विषय के माध्यम से सभी सूचनाओं को फ़नल करके देखने योग्य स्रोत को बढ़ाते हैं। आपको connect()विषय के साथ स्रोत की सदस्यता के लिए कॉल करने की आवश्यकता है ।

19
क्या आप का उपयोग कर सकते हैं क्यों getValue () एक लाल झंडा है? क्या होगा यदि मुझे केवल एक बार अवलोकन योग्य वर्तमान मूल्य की आवश्यकता है, जिस क्षण उपयोगकर्ता एक बटन क्लिक करता है? क्या मुझे मूल्य के लिए सदस्यता लेनी चाहिए और तुरंत सदस्यता समाप्त करनी चाहिए?
लौ

5
यह कभी-कभी ठीक है। लेकिन अक्सर यह संकेत होता है कि कोड का लेखक बहुत कुछ अनिवार्य कर रहा है (आमतौर पर साइड-इफेक्ट्स के साथ) जहां उन्हें नहीं होना चाहिए। आप click$.mergeMap(() => behaviorSubject.take(1))अपनी समस्या को हल करने के लिए भी कर सकते हैं ।
बेन लेश

1
महान व्याख्या! दरअसल, अगर आप ऑब्जर्वेबल रख सकते हैं और तरीकों का इस्तेमाल कर सकते हैं, तो यह बेहतर तरीका है। ऑब्जर्वेबल के साथ टाइपस्क्रिप्ट के संदर्भ में हर जगह उपयोग किया जाता है, लेकिन केवल कुछ ही व्यवहार व्यवहार के रूप में परिभाषित किया जाता है, तो यह कम सुसंगत कोड होगा। आपके द्वारा प्रस्तावित तरीकों ने मुझे हर जगह अवलोकन प्रकार रखने की अनुमति दी।
16

2
यह अच्छा है अगर आप बस वर्तमान मूल्य को भेजना चाहते हैं (जैसे कि इसे क्लिक पर सर्वर को भेजें), getValue () करना बहुत आसान है। लेकिन इसका उपयोग नहीं करते हैं जब वे अवलोकनीय ऑपरेटरों का पीछा करते हैं।
फ्लेवर्सस्केप

1
मेरे पास एक है AuthenticationServiceजो BehaviourSubjectराज्य में लॉग ( boolean trueया false) में वर्तमान लॉग को स्टोर करने के लिए उपयोग करता है । यह isLoggedIn$उन उपभोक्ताओं के लिए एक अवलोकन योग्य है, जो जानना चाहते हैं कि राज्य कब बदलता है। यह एक get isLoggedIn()संपत्ति को भी उजागर करता है , जो getValue()अंतर्निहित पर कॉल करके राज्य में वर्तमान लॉग इन लौटाता है BehaviourSubject- इसका उपयोग मेरे प्रमाणीकरण गार्ड द्वारा वर्तमान स्थिति की जांच करने के लिए किया जाता है। यह getValue()मेरे लिए एक समझदार उपयोग की तरह लगता है ...?
डैन किंग

11

मेरे पास ऐसी ही स्थिति थी जहां देर से ग्राहकों ने इसके मूल्य के आने के बाद सबजेक्ट की सदस्यता ली।

मुझे ReplaySubject मिला जो BehaviorSubject के समान है जो इस मामले में एक आकर्षण की तरह काम करता है। और यहाँ बेहतर स्पष्टीकरण के लिए एक लिंक है: http://reactivex.io/rxjs/manual/overview.html#replaybubject


1
इससे मुझे अपने Angular4 ऐप में मदद मिली - मुझे सब्सक्राइबर को कंस्ट्रक्टर कंस्ट्रक्टर से ngOnInit () (इस तरह के कंपोनेंट को रूटों में शेयर किया गया है) में ले जाना पड़ा, अगर किसी के पास भी ऐसा ही मुद्दा है तो छोड़ दें
Luca

1
मेरे पास एक कोणीय 5 ऐप के साथ एक मुद्दा था जहां मैं एक एपीआई से मूल्यों को प्राप्त करने और विभिन्न घटकों में चर सेट करने के लिए एक सेवा का उपयोग कर रहा था। मैं विषयों / वेधशालाओं का उपयोग कर रहा था लेकिन यह मार्ग परिवर्तन के बाद मूल्यों को आगे नहीं बढ़ाएगा। ReplaySubject सब्जेक्ट के प्रतिस्थापन में एक गिरावट थी और सब कुछ हल कर दिया।
jafaircl

1
सुनिश्चित करें कि आप ReplaySubject (1) का उपयोग कर रहे हैं अन्यथा नए ग्राहकों को अनुक्रम में हर पहले उत्सर्जित मूल्य मिलेगा - यह हमेशा रनटाइम पर स्पष्ट नहीं होता है
Drenai

@ ड्रेनई जहां तक ​​मैं समझता हूं रिप्लेस्बजेक्ट (1) बिहेवियरसुबजेक्ट () के समान व्यवहार करता है
केफिर इरेज

काफी समान नहीं है, बड़ा अंतर यह है कि ReplaySubject तुरंत डिफ़ॉल्ट मान का उत्सर्जन नहीं करता है जब यह सदस्यता लिया है कि यह next()फ़ंक्शन अभी तक लागू नहीं किया गया है, जबकि BehaviourSubject करता है। किसी दृश्य में डिफ़ॉल्ट मानों को लागू करने के लिए तत्काल उत्सर्जन बहुत आसान है, जब व्यवहार सेवा का उपयोग उदाहरण के लिए एक सेवा में डेटा स्रोत के रूप में किया जाता है
Drenai

4
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पाइप का उपयोग कर सकते हैं ।
इंगो बुर्क

2

एक समान दिखने वाले उत्तर को नीचा दिखाया गया। लेकिन मुझे लगता है कि मैं सीमित मामलों के लिए यहां जो कुछ भी सुझा रहा हूं, मैं उसे सही ठहरा सकता हूं।


हालांकि यह सच है कि एक अवलोकन योग्य वर्तमान मूल्य नहीं है , बहुत बार यह तुरंत उपलब्ध मूल्य होगा। उदाहरण के लिए, redux / flux / akita स्टोर्स के साथ आप कई वेधशालाओं के आधार पर एक केंद्रीय स्टोर से डेटा का अनुरोध कर सकते हैं और यह मान आमतौर पर तुरंत उपलब्ध होगा।

यदि ऐसा है तो जब आप subscribe, मान तुरंत वापस आ जाएगा।

तो मान लें कि आपके पास एक सेवा के लिए एक कॉल था, और पूरा होने पर आप अपने स्टोर से किसी चीज़ का नवीनतम मूल्य प्राप्त करना चाहते हैं, जो संभवतः नहीं निकल सकता है :

आप ऐसा करने की कोशिश कर सकते हैं (और जितना संभव हो आप चीजों को 'पाइप के अंदर' रखें):

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

                        // we have serviceCallResponse and customer 
                     });

इसके साथ समस्या यह है कि यह तब तक अवरुद्ध रहेगा जब तक कि माध्यमिक पर्यवेक्षित मूल्य का उत्सर्जन नहीं करता है, जो संभवतः कभी भी नहीं हो सकता है।

मैंने पाया कि हाल ही में एक अवलोकनीय का मूल्यांकन करने की आवश्यकता है यदि केवल एक मूल्य तुरंत उपलब्ध था , और इससे भी महत्वपूर्ण बात यह है कि मुझे यह पता लगाने में सक्षम होना चाहिए कि क्या नहीं था। मैंने यह करना समाप्त कर दिया:

 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मान प्राप्त करने के लिए उपयोग कर रहा हूँ (जैसा कि @ चर्चा करता है)। एक .valueसंपत्ति का उपयोग नहीं , भले ही मैं एक था BehaviorSubject


1
Btw यह काम करता है क्योंकि डिफ़ॉल्ट रूप से 'शेड्यूल' वर्तमान थ्रेड का उपयोग करता है।
सिमोन_विवर

1

हालांकि यह ओवरकिल लग सकता है, यह ऑब्जर्वेबल प्रकार रखने और बॉयलरप्लेट को कम करने के लिए सिर्फ एक और "संभव" समाधान है ...

ऑब्जर्व करने योग्य का वर्तमान मान प्राप्त करने के लिए आप हमेशा एक्सटेंशन गेट्टर बना सकते हैं ।

ऐसा करने के लिए आपको टाइपिंग डिक्लेरेशन फ़ाइल Observable<T>में इंटरफ़ेस का विस्तार करना होगा global.d.ts। तब लागू विस्तार गेटर एक में observable.extension.tsफ़ाइल और अंत में अपने आवेदन करने के लिए दोनों typings और एक्सटेंशन फ़ाइल शामिल हैं।

एक्सटेंशन को अपने कोणीय अनुप्रयोग में शामिल करने का तरीका जानने के लिए आप इस StackOverflow उत्तर का उल्लेख कर सकते हैं ।

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

आप पिछले उत्सर्जित मूल्य को ऑब्जर्वेबल से अलग स्टोर कर सकते हैं। फिर जरूरत पड़ने पर इसे पढ़ें।

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

एक सदस्यता बनाई जा सकती है और नष्ट होने वाली पहली उत्सर्जित वस्तु को लेने के बाद। पाइप एक फ़ंक्शन है जो एक ऑब्जर्वेबल को इसके इनपुट के रूप में उपयोग करता है और दूसरे ऑब्जर्वेबल को आउटपुट के रूप में वापस करता है, जबकि पहले ऑब्जर्वेबल को संशोधित नहीं करता है। कोणीय 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);
      });

    }
  }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.