问题
The root of my problem is displaying a loading indicator on http requests, and I want to do it at the service level without having to write code for each component. What I did was implement an http wrapper that basically does this:
getMyHttpObservable(...) {
setLoadingIndicator(true);
const myHttpObservable =
createRequestObservable().finally(()=> {
console.log('http done');
setLoadingIndicator(false);
});
return myHttpObservable;
}
I have a sequence of observables which I chain using Observable.concat
as follows:
const t1 = Observable.timer(5000).finally(()=>console.log('t1 done'));
const t2 = getMyHttpObservable(...);
const agg = Observable.concat(t1,t2);
I am using an event based cancellation technique using Observable.takeUntil
as follows:
const ev = new EventEmitter<any>();
const cancellableAgg = agg.takeUntil(ev.asObservable());
Now the problem is when I cancel the aggregate observable during the first timer period I do not get the finally call of the 2nd observable - the http request. Example:
const cancel = Observable.timer(500).finally(()=>ev.emit());
cancellableAgg.subscribe();
cancel.subscribe();
When the above runs, I get this output:
t1 done
And I need to get http done
as well, so that the loading indicator is cleared. How can I achieve that?
Thanks!
EDIT
I failed to realize that the concat
operator only subscribes to observables down the line, when their predecessors have completed. What I need is to activate my loading indicator only when the http observable has actually been subscribed to.
I modified my wrapper function as follows:
getMyHttpObservable(...) {
const myHttpObservable = createRequestObservable(...);
const subscriptionAware = new Observable(subscriber => {
setLoadingIndicator(true);
myHttpObservable.subscribe(
nextEv => subscriber.next(nextEv),
errEv => subscriber.error(errEv),
() => subscriber.complete())
.add(()=>setLoadingIndicator(false));
};
return subscriptionAware;
}
So basically, I'm creating an Observable
which transparently wraps another Observable
, but also performs some additional actions when it's actually subscribed to - activate the loading indicator.
However, now I've lost the ability to cancel XHR requests in flight, since the constructor of Observable
doesn't allow me to pass a handler for cancellation, so the inner subscription will never get cancelled.
Any ideas as to how to achieve that?
回答1:
Managed to find a solution using the built in defer
operator which waits for a subscription and returns a fresh observable:
getMyHttpObservable(...) {
const myHttpObservable = createRequestObservable(...);
return Observable.defer(() => {
setLoadingIndicator(true);
return myHttpObservable.finally(() => setLoadingIndicator(false));
});
}
Elegant and short.
来源:https://stackoverflow.com/questions/39190672/loading-indicator-on-http-requests