Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2, Concurrency in a shared service http observable

I have a shared service (starting from this suggestion) that cache and returns some data after first http request:

export class SharedService {
  constructor(private http:Http) {
  }

  getData() {
    if (this.cachedData) {
      return Observable.of(this.cachedData);
    } else {
      return this.http.get(...)
            .map(res => res.json())
            .do((data) => {
              this.cachedData = data;
            });
    }
  }
}

My problem is that I have some directives and components inside the same template that are all initialized at the same time and each one call simultaneously (inside ngInit function) the getData method (all of them before the first one succeeded) and so the service starts many http requests instead of returning cached data. Can someone suggest me how to avoid this side effect?

like image 421
Massimo Magliani Avatar asked Apr 14 '16 12:04

Massimo Magliani


2 Answers

Your solution doesn't cover all cases.

Compare it to my approach from https://stackoverflow.com/a/36291681/217408

getData() {
    if(this.data) {
      // if `data` is available just return it as `Observable`
      return Observable.of(this.data); 
    else if(this.observable) {
      // if `this.observable` is set then the request is in progress
      // return the `Observable` for the ongoing request
      return this.observable;
    } else {
      // create the request, store the `Observable` for subsequent subscribers
      this.observable = this.http.get('/someUrl')
          .map(res => res.json())
          .do(val => {
            this.data = val;
            // when the cached data is available we don't need the `Observable` reference anymore
            this.observable = null;
          })
          // make it shared so more than one subscriber can get the result
          .share();
      return this.observable;
    }
}

Until the request hasn't returned you need to return the Observable that was created from the first request to subsequent requests until the first requests completed.

https://stackoverflow.com/a/36296015/217408 also shows and interesting approach but with the small (depending on your requirments) drawback that requests can't be cancelled.

Also ensure you have registered your shared service only once as provider like explained by @MichaelD.

The Angular team recommends using the providers: [...] list of the root component instead of bootstrap(...) but there are no technical reasons. They consider it a bit more maintainable.

like image 126
Günter Zöchbauer Avatar answered Oct 28 '22 18:10

Günter Zöchbauer


You have to declare your service in the bootstrap method:

bootstrap(AppComponent, [SharedService]);

The service will be instanciated only once (singleton) so it should resolve your problem.

like image 1
Michael Desigaud Avatar answered Oct 28 '22 19:10

Michael Desigaud