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

102 Views Asked by At

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?

2

There are 2 best solutions below

0
BizzyBob On BEST ANSWER

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

0
Ja Da On

I mean personally I'd just have a BehaviorSubject that starts as null, like so:

const s = new BehaviorSubject<number | null>(null);
s.subscribe(x => {
  if (!x) 
    return;

  console.log(x)
});
s.next(1);