L. Heider
L. Heider

Reputation: 1819

How to create an Observable which updates it's value every 10 seconds (interval) only if subscribed?

I am building an angular app with RxJS. In my StoreService I want to have an Observable, which my components can subscribe from. The Observable public unreadMessagesCount: Observable<number> holds the value of unread messages coming from the api.

Every 10 seconds the api call should be done and the observable should update itself with the new value. BUT if no component has a subscription on this observable, the polling should not be done. And when someone subscribes, it should be done immediately. So that the data is up to date from the start.

Is there an elegant way to build this with RxJS? Thanks in advance!

Upvotes: 6

Views: 8519

Answers (4)

jo_va
jo_va

Reputation: 13964

You can use interval and switchMap to create an obsevable that will start to poll your API when you subscribe to it:

import { interval } from 'rxjs';

const unreadMessagesCount$ = interval(10000).pipe(switchMap(_ => apiCall()));

However, for every subscription you do, the API call will be duplicated. If you don't want this to happen, you have to share the source observable among all subscribers. You can use multicast for this:

import { interval, Subject } from 'rxjs';
import { multicast} from 'rxjs/operators';

const unreadMessagesCount$ = interval(10000).pipe(
  switchMap(_ => apiCall()),
  multicast(() => new Subject())
);

Then you simply need to call connect on the observable:

unreadMessagesCount$.subscribe(value => ...);
unreadMessagesCount$.subscribe(value => ...);

unreadMessagesCount$.connect();

See this Stackblitz demo.

Upvotes: 7

martin
martin

Reputation: 96891

You can use timer and just periodically make the async call with concatMap. If you want it to emit without the initial delay you can use timer(0, 10000) syntax:

import { timer } from 'rxjs';
import { concatMap } from 'rxjs/operators';

timer(0, 10000).pipe(
  concatMap(i => makeHTTPCall(i)),
);

Upvotes: 4

user4676340
user4676340

Reputation:

Use timer to create an observable emittiing every N seconds, starting at M seconds :

interval$ = timer(1, 10000);

You can then declare you API call :

APICall$ = this.http.get('...')

And finally, create an observable from thos two :

pulledData$ = this.interval$.pipe(
  switchMap(() => this.APICall$)
);

Now, unless you subscribe to it, you won't have any value. But once you subscribe to it, you make an API call (after 1ms though), and every ten seconds, you update your call. Just remember to unsuscribe when you destroy your component !

Upvotes: 5

Markus Kollers
Markus Kollers

Reputation: 3718

You could work with Subject or BehaviorSubject and timer:

import { BehaviorSubject, Observable, timer } from 'rxjs';
import { tap } from 'rxjs/operators';

...
export class YourService {
  value$ = new BehaviorSubject('your initial value');

  constructor() {
    timer(10000).pipe(
      tap(() => this.loadValue())
    ).subscribe();
  }

  loadValue() {
   callYourAPI().subscribe(
     value => this.value$.next(value);
   );
  }
}

Upvotes: 1

Related Questions