angulartypescriptrxjsangular-http

I need an Angular Observable returning single value but without initial value


I need something like an Observable that calls a webservice exactly once, and return its result to all subscribers. Subscriptions may happen before or after the call. How can I do that?

A Subject will give an answer to all clients that subscribed before, not after.

const s: Subject<number> = new Subject();
s.next(1);
s.subscribe(x => {console.log(x)}); // will not print anything

A BehaviourSubject requires a dummy initial value, and clients that subscribed before the call will get the dummy value instead of the right one.

const s: BehaviorSubject<number> = new BehaviorSubject(123);
s.subscribe(x => {console.log(x)}); // will print dummy value 123, then 1
s.next(1);

I tried creating my own Observable, however in this case the webservice could be called more than once

let val: number|undefined = undefined;
const s = new Observable((observer) => {
  if (val !== undefined) {
    observer.next(val);
  } else {
    doMyHttpCall().subscribe({
      response => {
        this.val = 1;
        observer.next(val);
    });
  }
});
s.subscribe(x => {console.log(x)});
s.subscribe(x => {console.log(x)});   // two clients may give two calls

What is the correct way to do that?


Solution

  • To share an observable response without making multiple calls to your webservice, you can simply use the shareReplay operator.

    export class WebService {
    
      data$ = this.http.get(ENDPOINT).pipe(
        shareReplay(1)
      );
    
      constructor(private http: HttpClient) { }
    
    }
    

    The first time a consumer subscribes to data$, the http.get() call will be made. Any subsequent subscribers will simply receive the replayed response.

    Here's a StackBlitz demo