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?