问题
I have two observables, one receives data from the browser localstorage and the other is from the database through WebAPI.
- 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.
- 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.
回答1:
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);
回答2:
You can use skipWhile and check for the data and return true or false.
observableObject.skipWhile((data)=> {
if(data){
return false;
}
});
来源:https://stackoverflow.com/questions/43151520/how-to-ignore-all-from-an-observable-if-the-other-observable-has-data-in-rxjs