简体   繁体   中英

How to ignore all from an observable if the other observable has data in RxJS?

I have two observables, one receives data from the browser localstorage and the other is from the database through WebAPI .

  1. I want to subscribe to them so if the observable from the localstorage has data, don't initiate the one to get the data from the database .
  2. If the observable from the localstorage does not have any data, invoke the ajax call to get the data from the WebAPI .

In the following example, I should get only 20, 40, 60, 80, 100 because the first observable has data. The second observable did not run because the first observable started emitting data.

在此处输入图片说明

You can use skipWhile and check for the data and return true or false.

observableObject.skipWhile((data)=> {
      if(data){
          return false;
      }
});

The local storage observable needs some way to signal that there is no data. If it just "hangs" and never completes, then you might use a timer to complete it:

// Use .amb() instead of .race() if your rxjs version is old
const timer = Observable.timer(1000).ignoreElements();
const lsObservable2 = Observable.race(lsObservable, timer);

This will start a timer and if the local storage observable does not produce a value within 1s, it will end the stream.

If your local storage observable will complete on its own if there is no data, then you can use it as is:

const lsObservable2 = lsObservable;

At this point, we'd really like to use defaultIfEmpty , because that has the semantics you want. Unfortunately it only supports a scalar value for the default, when instead you want to yield a different observable stream. So lets write our own version of defaultIfEmpty which produces a new stream using Observable.defer . We use defer so that each time someone subscribes, we can create a new closure variable ( hasValue ) and monitor whether or not the source observable produces a value for this subscription

Observable.prototype.defaultObservableIfEmpty = function(defaultObservable) {
    const source = this;
    return Observable.defer(() => {
        let hasValue = false;
        // create a deferred observable that will evaluate to
        // defaultObservable if we have not seen any values, or
        // empty observable if we have seen any values.
        const next = Observable.defer(() => hasValue ? Observable.empty() : defaultObservable);

        // now use do() to set hasValue to true if we see a value from
        // the source observable
        const sourceSetsValue = source.do(v => hasValue = true);

        // now we can can just concat this sourceSetsValue
        // with out "next" observable.  When the first observable
        // finishes, it will subscribe to "next", which will then
        // either produce the defaultObservable or an empty observable
        return sourceSetsValue.concat(next);
    });
}

Next, let's assume you've setup your db Observable to not issue the ajax call until it is actually subscribed. This is an important step. Again you can use something like defer :

const dbObservable = Observable.defer(() => makeDbCall());

Then we can use your new operator like so:

const data = lsObservable2.defaultObservableIfEmpty(dbObservable);

So your application code looks like this (once you've added the new operator to your library):

const timer = Observable.timer(1000).ignoreElements();
const lsObservable2 = Observable.race(lsObservable, timer);
const dbObservable = Observable.defer(() => makeDbCall());
const data = lsObservable2.defaultObservableIfEmpty(dbObservable);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM