Reputation: 1819
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
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
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
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
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