0

(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]

  1. 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.
  2. If the data is not in memory, but we started the GET, return the stored observable.
  3. 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$;              
}

.....
John Pankowicz
  • 4,203
  • 2
  • 29
  • 47
  • Yes @Günter it is a duplicate. I do not want to either edit it or ask a new question. I would prefer to delete it, but I am warned that I should never delete a question that already has an answer. – John Pankowicz May 12 '16 at 10:39
  • No need to delete it. If people with a similar problem land on this question because they search terms match this question better then the linked one, then they are still pointed to the solution. This just improves the value of SO. – Günter Zöchbauer May 12 '16 at 10:41
  • Günter's code for solving this is definitely a lot simpler that mine. He basically uses the same logic but he returns a SHARED observable. I will update my question to include the missing logic. – John Pankowicz May 15 '16 at 12:27
  • @Günter I took your advise and I didn't delete it. Now I see that I am having points deducted for the question. And I don't even know why they are being deducted. No one marked the question down. – John Pankowicz Jun 22 '16 at 15:05
  • The question was downvoted once and I upvoted it which should result in a net of +8. It's hard to know when no comment was added about what someone thinks is wrong with a question. I try to ignore downvotes without comments when I'm not aware of any wrongdoing. Duplicate questions don't cause any harm especially when they are marked as duplicate. It's your call if you want to delete it. – Günter Zöchbauer Jun 22 '16 at 15:09
  • The way this question was asked allowed me to better understand the original answer (plus it showed up at the top of my search), so it definitely has value. – Patrick Chu Jul 07 '17 at 17:05

1 Answers1

0

I think that the logic you have outlined is the right one. Like it or not (and I do not necessarily like it) this is an async world. So either you go with your 1 2 3 steps or you try to check synchronously some sort of local cache but then, if data is not there, we are back in the in the async world. I hope this can help and, by the way, if somebody knows a different way I would be very happy to hear

Picci
  • 16,775
  • 13
  • 70
  • 113