Keepalive
Keepalive is a repetitive signal firing at a predefined interval.
The observable should look like this:
--k--k--k--k--|
Where "k" is the keepalive event that fires at an interval of 20 frames.
Interval
The interval itself is achieved using Observable.interval
.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-interval
Suppose we abstract the logic for the keepalive (e.g. calling an API) within a service which returns an Observable: service.keepalive()
.
Then we use a simple switchMap
to trigger the keepalive on an interval:
Observable
.interval(KEEP_ALIVE_INTERVAL)
.switchMap(() => service.keepAlive());
Initiation
We want to kick off the interval with another event:
START ---s----------
INTERVAL -----k--k--k--
Suppose we use ngrx for our start action (KEEP_ALIVE_START
):
@Effect() keepAlive$: Observable<Action> = updates$
.whenAction(KEEP_ALIVE_START)
.switchMap(() => {
return Observable
.interval(KEEP_ALIVE_INTERVAL)
.switchMap(() => service.keepAlive());
});
Stopping
Now we need to stop the keepalive interval.
For this, we can use takeUntil
which will complete an observable when a given observable fires an element.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-takeUntil
START ---s------------
INTERVAL -----k--k--k----
STOP -------------x--
RESULT -----k--k--k-|
Suppose we use ngrx for our stop action (KEEP_ALIVE_STOP
):
@Effect() keepAlive$: Observable<Action> = updates$
.whenAction(KEEP_ALIVE_START)
.switchMap(() => {
return Observable
.interval(KEEP_ALIVE_INTERVAL)
.switchMap(() => service.keepAlive())
.takeUntil(updates$.whenAction(KEEP_ALIVE_STOP));
});
Background
What if we want the keepalive to run in the background without emitting elements for every "ping"?
For this, we use ignoreElements
.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-ignoreElements
START ---s------------
INTERVAL -----k--k--k----
STOP -------------x--
RESULT -------------|
@Effect() keepAlive$: Observable<Action> = updates$
.whenAction(KEEP_ALIVE_START)
.switchMap(() => {
return Observable
.interval(KEEP_ALIVE_INTERVAL)
.switchMap(() => {
return service.keepAlive()
.ignoreElements();
})
.takeUntil(updates$.whenAction(KEEP_ALIVE_STOP));
});
Failures
What if we want to emit an element every time a keepalive ping fails?
In the exmaple below, the second ping fails:
START ---s------------
INTERVAL -----k--k--k----
STOP -------------x--
RESULT --------f----|
We use catch
to achieve this:
@Effect() keepAlive$: Observable<Action> = updates$
.whenAction(KEEP_ALIVE_START)
.switchMap(() => {
return Observable
.interval(KEEP_ALIVE_INTERVAL)
.switchMap(() => {
return service.keepAlive()
.ignoreElements()
.catch((response) => Observable.of(KEEP_ALIVE_FAIL));
})
.takeUntil(updates$.whenAction(KEEP_ALIVE_STOP));
});