RxJS - Stop Observable During an Interval

08/22/2021, Sun
Categories: #JavaScript
Tags: #RxJS

Replicate Pause on an Observable Trigger

A way to simulate a pause of an observable from triggering is to stop it and to restart it.

One might wish to pause an observable because the observable might have activated the run of an expensive or long-running operation such a mathematical computation or a network request and there is a need to have a temporary block on the triggering observable.

Breaking down the running of a desired observable into two parts for activation when dealing with a blocking observable event, replicates the pausing of the initial observable event. The triggering observable will have a "before" and "after" phase regarding the pausing event.

To illustrate this behavior, the example below will emulate a pause with the use of a timer. When the timer is active, the desired behavior of being able to press the letter 'a' key will not work.

To replicate a timer observable, a RxJs timer should be coupled with a RxJs interval. For the ease of demonstration, the timer will actually serve as a delay, so one can test out the press of the letter 'a' key before the pause kicks in. The interval will play the role of stepping through the seconds in the time period and after this interval count ends, the press of the letter 'a' is restored.

const { fromEvent, timer, interval } = rxjs;
const { take, filter, concatMapTo, takeLast, takeUntil, mergeMap } = rxjs.operators;

const keyboardDown$ = fromEvent(document, "keydown").pipe(
  filter((event) => !event.repeat)
);

const aDown$ = keyboardDown$.pipe(filter((event) => event.code === "KeyA"));

const timer$ = timer(3000);

const interval$ = interval(1000).pipe(take(3));

const timing$ = timer$.pipe(concatMapTo(interval$));

const timerEnd$ = timing$.pipe(takeLast(1));

// Treat the timer's interval counting as an expensive operation.
// Pressing the 'a' key might trigger an heavy computation somewhere,
// and you would like to stop further presses of the 'a' key while
// the processing is still taking place.
timing$.subscribe(() => {
  console.log('A second has passed after the initial delay of 3 seconds.');
});

timerEnd$.subscribe(() => {
  console.log('Timer ends.');
});

// Allow only the letter 'a' to be press when the timer has not
// started counting and after the timer has completed.
const aDownAfterTimerEnds$ = timerEnd$
  .pipe(mergeMap(() => {
    return aDown$;
  }));

const aDownBeforeTimerStarts$ = aDown$
  .pipe(
  	takeUntil(timing$)
  );

aDownBeforeTimerStarts$.subscribe(() => {
  console.log("'A' key pressed before timer starts.");
});

aDownAfterTimerEnds$.subscribe(() => {
  console.log("Restored the letter 'A' key press");
});