1

I need to cache server response in my service. I checked this question caching results with angular2 http service, here I have found 2 ways of doing that 1) Observable.share() - but as it said in the answer "he share() operator works just on the first request, when all the subscriptions are served and you create another one, then it will not work, it will make another Request" 2) use ReplaySubject works nice until using resolvers (it's creating new requests). Here is my plunker https://plnkr.co/edit/TiODzGyQtXepojf4oPgw you can check network tab, both ways creating new requests when you navigate from component A to component B. Any idea how to solve this problem?

my service

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import * as Rx from 'rxjs';
import { Http } from '@angular/http';
import { ReplaySubject } from 'rxjs/ReplaySubject';

@Injectable()
export class ContactsService {
  goals: ReplaySubject<Array<any>> = new ReplaySubject(1);
  constructor(private http: Http) {}
  get() {
    if (!this.goals.observers.length) {
      return this.http
        .get('https://jsonplaceholder.typicode.com/posts/1');
    }

    return this.goals;
  }

  get2() {
    return this.http
        .get('https://jsonplaceholder.typicode.com/posts/1').share();
  }
}

Update

for subjects you can use the following method (suggested by ehrencrona)

cache: ReplaySubject<Array<any>>

get() {
  if (!this.cache) {
    this.cache = new ReplaySubject(1) 
    this.http.get(url).subscribe(this.cache)
  }

  return this.cache
}

for observable you can use this method I found out

observable: Observable<any>;
get() {
    if (!this.observable) {
      this.observable = this.http.get(url).publishReplay(1).refCount();
    }

    return this.observable;
  }

Notice that .publishReplay(1).refCount() is not the same as .share() - with .share() it't creating new requests

1
  • Please note, if you put a console.log before the http.get, it never gets called. So !this.dataObs$.observers.length does not seem like the best way test if the ReplaySubject has a value. It counts the number of subscribers. Commented Nov 21, 2017 at 3:37

1 Answer 1

3

The standard pattern for caching a fetched value using RxJS would be as follows:

cache: ReplaySubject<Array<any>>

get() {
  if (!this.cache) {
    this.cache = new ReplaySubject(1) 
    this.http.get(url).subscribe(this.cache)
  }

  return this.cache
}

The cache instance stores the values that have been fetched. When you start out, the instance is not set, which indicates that a value has never been fetched before. When you first call get(), it will create a ReplaySubject and let it "play back" the values it gets from the AJAX call by having it subscribe to the original observable returned by http.get.

Then returned value is always the cache.

Note that this pattern solves a common problem when caching: if a second caller call the method before the first method has finished retrieving it, it will still only do the HTTP call once. If you just cached the retrieved value rather than an observable that would be hard to achieve.

Sign up to request clarification or add additional context in comments.

5 Comments

that looks correct, can you add a code for observable?
get() { if (!this.observable) { this.observable = this.apiService.get('api_v3_secured_financialgoal_get'); } return this.observable; } this way creating new requests. Please check my updated plunker, so I can accept your answer
A subject implements the observable interface, so you can return it to callers that expect an observable. There is no need for a separate implementation with a different type.
yea, but I don't want to create a subject when I'm not going to update this data, it doesn't make any sense, does it? That's why I created different solution for observables
The data needs to be stored somewhere. ReplaySubject is the class intended for storing and "replaying" observable sequences. So it makes sense to use it here. Other solutions are of course possible, but I don't see any disadvantages to this one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.