(In an Angular 2 app): If the data is already in memory, just return it, otherwise do a HTTP GET. It sounds like it should be simple. Is there any easier way than the following?
[LOGIC]
- If the data is not in memory and we haven't started the GET, start the GET, return an observable and also store the observable.
- If the data is not in memory, but we started the GET, return the stored observable.
- If the data is in memory, return a new observable which immediately returns the data.
[Update 1] Apparently this logic is not working. I see GET requests being sent every time getTopics is called! -- even though the http.get method only gets executed once. Each time a new caller gets a copy of the original observable, another GET request is sent!
[Update 2] I just learned that "nothing happens on an observable until you call subscribe". This explains why the GET requests are getting called when I return the observable. The callers then immediately call subscribe. This means that my logic is definitely wrong and I am likely back at square one in solving this "simple looking" problem.
[Update 3] Günter's solution includes the very important step of returning a "shared" observable. This solves the issue of sending multiple GET requests. The code below does NOT include this.
[CODE] (the data is an array of "topics")
@Injectable()
export class TopicsService {
private _topicsUrl = 'app/topics/topics.json';
private _topics: string[] = null;
private _topics$: Observable<string[]> = null;
constructor (private http: Http) {}
getTopics(): Observable<string[]> {
// if topics are in memory, return them.
if (this._topics != null) {
return this.getTopicsTFromMem();
// otherwise get them from the file.
} else {
return this.getTopicsFromFile();
}
}
// return topics from memory. Actually return an observable which will return them.
getTopicsTFromMem(): Observable<string[]> {
let topics$ = new Observable(observer => {
observer.next(this._topics);
});
return topics$;
}
// return topics from file. Actually return an observable which will return them.
getTopicsFromFile(): Observable<string[]> {
// If someone already started the http GET, then return existing observable.
if (this._topics$ != null) {
return this._topics$;
}
// otherwise start the GET
let topics$ = this.http.get(this._topicsUrl)
.map(this.extractData)
.catch(this.handleError);
topics$.subscribe(topics => {this._topics = topics;});
this._topics$ = topics$;
return topics$;
}
.....