कैसे एक ऑब्जर्व करने योग्य अनुक्रम दूसरे को छोड़ने से पहले पूरा होने की प्रतीक्षा करें?


86

कहो मेरे पास एक है Observable, जैसे:

var one = someObservable.take(1);

one.subscribe(function(){ /* do something */ });

फिर, मेरे पास एक दूसरा है Observable:

var two = someOtherObservable.take(1);

अब, मैं चाहता हूँ subscribe()करने के लिए twoहै, लेकिन मुझे लगता है कि यह सुनिश्चित करना चाहते oneसे पहले पूरा कर लिया है twoग्राहक निकाल दिया जाता है।

किस तरह की बफरिंग विधि का उपयोग करने के twoलिए मैं पहले एक के पूरा होने का दूसरा इंतजार कर सकता हूं ?

मुझे लगता है कि मैं पूरा twoहोने तक रुकने की तलाश कर रहा हूं one


1
मेरा मानना ​​है कि इसका उत्तर .exerateMap () विधि है, लेकिन मैं यह जानने का नाटक नहीं करूंगा कि इसे कैसे लागू किया जाए - पूरा विवरण यहां: blog.angular-university.io/rxjs-higher-order-mapping
पीटर निक्स

जवाबों:


53

कुछ तरीके जिनसे मैं सोच सकता हूं

import {take, publish} from 'rxjs/operators'
import {concat} from 'rxjs'

//Method one

var one = someObservable.pipe(take(1));
var two = someOtherObservable.pipe(take(1));
concat(one, two).subscribe(function() {/*do something */});

//Method two, if they need to be separate for some reason
var one = someObservable.pipe(take(1));
var two = someOtherObservable.pipe(take(1), publish());
two.subscribe(function(){/*do something */});
one.subscribe(function(){/*do something */}, null, two.connect.bind(two));

1
मैं का उपयोग कर समाप्त हो गया pauseऔर resumeबजाय publishऔर connect, लेकिन उदाहरण दो अनिवार्य रूप से मार्ग मैं ले लिया है।
स्टीफन

1
क्या यह विधि हमेशा (-) सदस्यता के अंदर oneदूसरे ( two) से पहले पहले अवलोकन योग्य ( ) संकल्प करेगी ?
जॉन

उपयोग क्यों नहीं Observable.forkJoin()? इस लिंक को देखें learnrxjs.io/operators/combination/forkjoin.html
mspasiuk

16
ओपी आवश्यकता के अनुसार @mspasiuk, वे केवल पहले पूरा होने के बाद दूसरी सदस्यता चाहते थे । forkJoinएक साथ सदस्यता।
पॉलपडनील्स

17

यदि आप यह सुनिश्चित करना चाहते हैं कि निष्पादन के क्रम को बनाए रखा जाए तो आप निम्न उदाहरण के रूप में फ्लैटपाइप का उपयोग कर सकते हैं

const first = Rx.Observable.of(1).delay(1000).do(i => console.log(i));
const second = Rx.Observable.of(11).delay(500).do(i => console.log(i));
const third = Rx.Observable.of(111).do(i => console.log(i));

first
  .flatMap(() => second)
  .flatMap(() => third)
  .subscribe(()=> console.log('finished'));

परिणाम होगा:

"1"
"11"
"111"
"finished"

15

SkipUntil () अंतिम के साथ ()

SkipUntil: उत्सर्जित वस्तुओं को तब तक अनदेखा करें जब तक कि एक और अवलोकनीय उत्सर्जित न हो जाए

अंतिम: एक अनुक्रम से अंतिम मान का उत्सर्जन करें (यानी तब तक प्रतीक्षा करें जब तक वह पूर्ण न हो जाए)

ध्यान दें कि पर्यवेक्षित से निकली कोई भी चीज skipUntilलंघन को रद्द कर देगी, यही कारण है कि हमें जोड़ने की जरूरत है last()- धारा को पूरा करने के लिए प्रतीक्षा करने के लिए।

main$.skipUntil(sequence2$.pipe(last()))

आधिकारिक: https://rxjs-dev.firebaseapp.com/api/operators/skipUntil


संभावित समस्या: ध्यान दें कि यदि कुछ भी उत्सर्जित नहीं होता है, तो last()स्वयं त्रुटि करेगा last()ऑपरेटर एक है defaultपैरामीटर लेकिन केवल तभी जब विधेय के साथ संयोजन के रूप में इस्तेमाल किया। मुझे लगता है कि अगर यह स्थिति आपके लिए एक समस्या है (यदि आप sequence2$बिना किसी समस्या के पूरा कर सकते हैं) तो इनमें से एक को काम करना चाहिए (वर्तमान में अप्राप्त):

main$.skipUntil(sequence2$.pipe(defaultIfEmpty(undefined), last()))
main$.skipUntil(sequence2$.pipe(last(), catchError(() => of(undefined))

ध्यान दें कि undefinedउत्सर्जित होने वाली एक मान्य वस्तु है, लेकिन वास्तव में इसका कोई मूल्य हो सकता है। यह भी ध्यान दें कि यह पाइप से जुड़ा हुआ है sequence2$न कि main$पाइप।


बहुत ही भद्दा डेमो: कोणीय- gzznak.stackblitz.io आपको कंसोल ट्रे खोलने के लिए क्लिक करने की आवश्यकता है
Simon_Weaver

आपका वाक्यविन्यास गलत है। SkipUntil को सीधे एक नमूदार से नहीं जोड़ा जा सकता है अन्यथा आपको निम्नलिखित त्रुटि मिलेगी: 'संपत्ति' SkipUntil 'का प्रकार' Observable <any> 'पर मौजूद नहीं है। आपको इसे पहले .pipe ()
London804

हाँ, पाइप की आवश्यकता होने से पहले यह एक पुराना उत्तर है। इसका उल्लेख करने के लिए धन्यवाद। मैं इसे अभी अपडेट करूंगा लेकिन मैं अपने फोन पर हूं। जवाब को संपादित करने के लिए स्वतंत्र महसूस करें।
सिमोन_विवर

13

स्विचपाइप के परिणाम चयनकर्ता का लाभ उठाने की एक और संभावना है

var one$ = someObservable.take(1);
var two$ = someOtherObservable.take(1);
two$.switchMap(
    /** Wait for first Observable */
    () => one$,
    /** Only return the value we're actually interested in */
    (value2, value1) => value2
  )
  .subscribe((value2) => {
    /* do something */ 
  });

चूंकि switchMap के परिणाम चयनकर्ता को ह्रास किया गया है, इसलिए यहां एक अद्यतन संस्करण है

const one$ = someObservable.pipe(take(1));
const two$ = someOtherObservable.pipe(
  take(1),
  switchMap(value2 => one$.map(_ => value2))
);
two$.subscribe(value2 => {
  /* do something */ 
});

8

यहाँ यह करने का एक पुन: प्रयोज्य तरीका है (यह टाइपस्क्रिप्ट है लेकिन आप इसे js के लिए अनुकूलित कर सकते हैं):

export function waitFor<T>(signal: Observable<any>) {
    return (source: Observable<T>) =>
        new Observable<T>(observer =>
            signal.pipe(first())
                .subscribe(_ =>
                    source.subscribe(observer)
                )
        );
}

और आप इसे किसी भी ऑपरेटर की तरह उपयोग कर सकते हैं:

var two = someOtherObservable.pipe(waitFor(one), take(1));

यह मूल रूप से एक ऑपरेटर है जो स्रोत पर ग्राहकी को तब तक देखने योग्य बनाता है जब तक कि संकेत अवलोकन योग्य पहली घटना का उत्सर्जन नहीं करता।


6

यदि दूसरा देखने योग्य गर्म है , तो ठहराव / फिर से शुरू करने का एक और तरीका है :

var pauser = new Rx.Subject();
var source1 = Rx.Observable.interval(1000).take(1);
/* create source and pause */
var source2 = Rx.Observable.interval(1000).pausable(pauser);

source1.doOnCompleted(function () { 
  /* resume paused source2 */ 
  pauser.onNext(true);
}).subscribe(function(){
  // do something
});

source2.subscribe(function(){
  // start to recieve data 
});

इसके अलावा आप बफ़र्ड संस्करण का उपयोग कर सकते हैं pausableBuffered डेटा को पॉज़ के दौरान चालू रखने के लिए।


2

यहाँ अभी तक एक और है, लेकिन मैं अधिक सीधा और सहज महसूस करता हूं (या कम से कम प्राकृतिक अगर आप वादे के लिए उपयोग किए जाते हैं), दृष्टिकोण। मूल रूप से, आप Observable.create()रैप करने के लिए oneऔर twoएक ही ऑब्जर्वेबल के रूप में एक ऑब्जर्वेबल बनाते हैं । यह Promise.all()काम करने के तरीके के समान है ।

var first = someObservable.take(1);
var second = Observable.create((observer) => {
  return first.subscribe(
    function onNext(value) {
      /* do something with value like: */
      // observer.next(value);
    },
    function onError(error) {
      observer.error(error);
    },
    function onComplete() {
      someOtherObservable.take(1).subscribe(
        function onNext(value) {
          observer.next(value);
        },
        function onError(error) {
          observer.error(error);
        },
        function onComplete() {
          observer.complete();
        }
      );
    }
  );
});

तो, यहाँ क्या हो रहा है? सबसे पहले, हम एक नया ऑब्जर्वेबल बनाते हैं। इस समारोह को Observable.create()नाम दिया गया , जिसे उपयुक्त नाम onSubscriptionदिया गया है, पर्यवेक्षक (आपके द्वारा पास किए गए मापदंडों से निर्मित subscribe()) को पारित किया जाता है , जो एक नया वादा करते समय एक ही वस्तु के समान resolveऔर rejectसंयुक्त होता है। इस तरह हम जादू का काम करते हैं।

में onSubscription, हम पहले ऑब्जर्वेबल की सदस्यता लेते हैं (ऊपर के उदाहरण में, इसे कहा जाता था one)। हम आपको कैसे संभालते हैं nextऔर errorआप पर निर्भर हैं, लेकिन मेरे नमूने में प्रदान की गई डिफ़ॉल्ट आम तौर पर बोलना उचित होना चाहिए। हालाँकि, जब हम completeघटना को प्राप्त करते हैं , जिसका अर्थ है oneकि अब किया जाता है, तो हम अगले अवलोकन के लिए सदस्यता ले सकते हैं; इसके बाद दूसरा ऑब्जर्वेबल फायरिंग के बाद पहले वाला पूरा हो गया।

दूसरे प्रेक्षक के लिए प्रदान किया गया उदाहरण प्रेक्षक काफी सरल है। मूल रूप से, secondअब वह कार्य करता है जो आप twoओपी में कार्य करने की अपेक्षा करेंगे । अधिक विशेष रूप से, (क्योंकि ) के द्वारा और उसके बाद पहले और बाद में पूर्ण होने secondवाले पहले मान का उत्सर्जन होगा, यह मानते हुए कि कोई त्रुटि नहीं है।someOtherObservabletake(1)

उदाहरण

यहाँ एक पूर्ण, कार्यशील उदाहरण है जिसे आप कॉपी / पेस्ट कर सकते हैं यदि आप मेरे उदाहरण को वास्तविक जीवन में काम करते देखना चाहते हैं:

var someObservable = Observable.from([1, 2, 3, 4, 5]);
var someOtherObservable = Observable.from([6, 7, 8, 9]);

var first = someObservable.take(1);
var second = Observable.create((observer) => {
  return first.subscribe(
    function onNext(value) {
      /* do something with value like: */
      observer.next(value);
    },
    function onError(error) {
      observer.error(error);
    },
    function onComplete() {
      someOtherObservable.take(1).subscribe(
        function onNext(value) {
          observer.next(value);
        },
        function onError(error) {
          observer.error(error);
        },
        function onComplete() {
          observer.complete();
        }
      );
    }
  );
}).subscribe(
  function onNext(value) {
    console.log(value);
  },
  function onError(error) {
    console.error(error);
  },
  function onComplete() {
    console.log("Done!");
  }
);

यदि आप कंसोल देखते हैं, तो उपरोक्त उदाहरण प्रिंट होगा:

1

6

किया हुआ!


यह वह सफलता थी जिसके लिए मुझे अपना स्वयं का कस्टम 'क्लस्टर (T, X, D)' ऑपरेटर बनाने की आवश्यकता थी जो स्रोत से timespan टी के भीतर केवल पहले X का उत्सर्जन करता है और D देरी से परिणाम निकलता है। धन्यवाद!
winkim00

मुझे खुशी है कि इसने मदद की, यह बहुत ही ज्ञानवर्धक था जब मुझे इस बात का एहसास हुआ।
c1moore

2

यहाँ एक कस्टम ऑपरेटर टाइपस्क्रिप्ट के साथ लिखा गया है जो परिणामों को छोड़ने से पहले सिग्नल की प्रतीक्षा करता है:

export function waitFor<T>(
    signal$: Observable<any>
) {
    return (source$: Observable<T>) =>
        new Observable<T>(observer => {
            // combineLatest emits the first value only when
            // both source and signal emitted at least once
            combineLatest([
                source$,
                signal$.pipe(
                    first(),
                ),
            ])
                .subscribe(([v]) => observer.next(v));
        });
}

आप इसे इस तरह से उपयोग कर सकते हैं:

two.pipe(waitFor(one))
   .subscribe(value => ...);

1
अच्छा पैटर्न! तुम भी कर सकते हैं तीन। पकाने की विधि (WaitFor (एक), WaitFor (दो), ले (1)
डेविड रिंक

1

खैर, मुझे पता है कि यह बहुत पुराना है, लेकिन मुझे लगता है कि आपको इसकी आवश्यकता हो सकती है:

var one = someObservable.take(1);

var two = someOtherObservable.pipe(
  concatMap((twoRes) => one.pipe(mapTo(twoRes))),
  take(1)
).subscribe((twoRes) => {
   // one is completed and we get two's subscription.
})

0

आप पिछले ऑब्जर्वेबल थैंक्स से विलय किए गए परिणाम ( जैसे उसका उपनाम फ्लैटमैप ) ऑपरेटर के लिए इस तरह से उपयोग कर सकते हैं :

 const one = Observable.of('https://api.github.com/users');
 const two = (c) => ajax(c);//ajax from Rxjs/dom library

 one.mergeMap(two).subscribe(c => console.log(c))

यहाँ से: learnrxjs.io/learn-rxjs/operators/transformation/mergemap - "यदि आंतरिक वेधशालाओं के उत्सर्जन और सदस्यता का क्रम महत्वपूर्ण है, तो कॉन्‍टैपट को आज़माएं!"
gsziszi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.