简体   繁体   中英

Angular service instantiated every time by injection to component

I implemented a service which should fetch data once and cache the result. The service is at root level. Every time I inject this service in a constructor of a component and subscribe to the method the service make a new http request.

I already tried to put the request code out of the constructor to the method.

@Injectable({
  providedIn: 'root'
})
export class SessionService {
  private api = Configuration.api + 'session/';
  private readonly session$: Observable<Session>;

  constructor(private http: HttpClient) {
    this.session$ = this.http
      .get<any>(`${this.api}current`, {withCredentials: true})
      .pipe(
        timeout(Configuration.timeout),
        retry(Configuration.retry),
        map(data => {
            return data.session as Session;
          }
        ),
        catchError(ErrorService.handleError)
      );
  }

  getCurrentSession(): Observable<Session> {
    return this.session$;
  }
}

export class Component implements OnInit, AfterContentInit {
session = Session.empty();

  constructor(
    private _s: SessionService) {
    this._s.getCurrentSession().subscribe(session => this.session = session);
  }
}

The aim is to do a request once and cache the result in a variable.

observables are definitions of a stream, they execute on every subscription, so everytime you're just subscribing to the http request which causes it to execute. to get around this, you need to subscribe in your constructor and store the result in a subject that will handle the caching:

export class SessionService {
  private api = Configuration.api + 'session/';

  private session$ = new ReplaySubject<Session>(1);

  constructor(private http: HttpClient) {
    this.loadSession();
  }

  loadSession() {
    this.http
      .get<any>(`${this.api}current`, {withCredentials: true})
      .pipe(
        timeout(Configuration.timeout),
        retry(Configuration.retry),
        map(data => {
            return data.session as Session;
          }
        ),
        catchError(ErrorService.handleError)
      ).subscribe(this.session$);
  }

  getCurrentSession(): Observable<Session> {
    return this.session$.asObservable();
  }
}

though you could also get away with doing this:

this.session$ = this.http
  .get<any>(`${this.api}current`, {withCredentials: true})
  .pipe(
    timeout(Configuration.timeout),
    retry(Configuration.retry),
    map(data => {
        return data.session as Session;
      }
    ),
    catchError(ErrorService.handleError),
    shareReplay(1)
  );

the shareReplay operator more or less does exactly the same thing. The reason I prefer the original method I suggested is because it gives an easier way to force the data to reload if needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM