I've found a number of approaches to cache reactive observables and, more specifically, the results of http requests. However, I am not fully satisfied with the proposed solutions because of the reasons below:
1. This answer https://stackoverflow.com/a/36417240/1063354 uses a private field to store the result of the first request and reuses it in all subsequent calls.
the code:
private data: Data;
getData() {
if(this.data) {
return Observable.of(this.data);
} else {
...
}
}
The sad thing is that the power of observables is completely ignored - you do all the stuff manually. In fact I wouldn't look for a proper solution if I was satisfied with assigning the result to a local variable/field. Another important thing which I consider a bad practice is that a service should not have a state - i.e. should have no private fields containing data which are changed from call to call. And it's fairly easy to clear the cache - just set this.data to null and the request will be reexecuted.
2. This answer https://stackoverflow.com/a/36413003/1063354 proposes to use ReplaySubject:
private dataObs$ = new ReplaySubject(1);
constructor(private http: Http) { }
getData(forceRefresh?: boolean) {
// If the Subject was NOT subscribed before OR if forceRefresh is requested
if (!this.dataObs$.observers.length || forceRefresh) {
this.http.get('http://jsonplaceholder.typicode.com/posts/2').subscribe(
data => this.dataObs$.next(data),
error => {
this.dataObs$.error(error);
// Recreate the Observable as after Error we cannot emit data anymore
this.dataObs$ = new ReplaySubject(1);
}
);
}
return this.dataObs$;
}
Looks pretty awesome (and again - no problem to clear the cache) but I am not able to map the result of this call, i.e.
service.getData().map(data => anotherService.processData(data))
which happens because the underlying observer has not called its complete method. I'm pretty sure that a lot of reactive methods won't work here as well. To actually get the data I have to subscribe to this observable but I don't want to do it: I want to get the cached data for one of my components via a resolver which should return an Observable (or Promise), not a Subscription:
The route
{
path: 'some-path',
component: SomeComponent,
resolve: {
defaultData: DefaultDataResolver
}
}
The Resolver
...
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Data> {
return this.service.getData();
}
The component is never activated because its dependency is never resolved.
3. Here https://stackoverflow.com/a/36296015/1063354 I found a proposal to use publishLast().refCount().
the code:
getCustomer() {
return this.http.get('/someUrl')
.map(res => res.json()).publishLast().refCount();
}
This satisfies my demands for both caching and resolving BUT I haven't found a clean and neat solution to clear the cache.
Am I missing something? Could anyone think out a better way to cache reactive observables being able to map their results as well as refresh the cached data once it's no longer relevant?