RxJS Mastery – #14 interval

RxJS interval operator lesson title

The RxJS interval operator emits numbers every specified interval of time. This allows to execute functionality frequently in well specified time frames. As the operator only emits numbers, you need to do some mapping if needed.

RxJS interval explained 🎓

The interval operator creates an Observable:

interval(period: number = 0, scheduler: SchedulerLike = asyncScheduler): Observable<number>

The period parameter defines the interval size in milliseconds. So, for one second you would specify interval(1000). As a second parameter a scheduler can be passed to adjust the notion of time. If you want the emission to finish you need to cancel the Observable. For that you can for example use the take() operator:

interval(1)
    .pipe(take(3))
    .subscribe({ next: console.log });
// emits 0,1,2

What problems does the RxJS interval operator solve? 🚧

Anytime you need to create an Observable that should emit values in a regular time-based interval, you should use interval. For example you could implement a polling mechanism with interval, e.g. executing an HTTP request every second.

How to test the interval operator🚦

RxJS marbles operates with 1 millisecond time spans as a default. Hence, testing code involving interval(1) is quite easy. In the example below we are applying the take operator to only consider a certain amount of emissions. Noticeable is that the emission only starts after 1 millisecond. That is why we have added a ‘-‘ to the marble diagram string.

it('should emit every millisecond', () => {
    testScheduler.run((helpers) => {
        const { expectObservable } = helpers;

        const numbers$ = interval(1).pipe(take(5));

        expectObservable(numbers$).toBe('-0123(4|)', {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4});
    });
});

But what if we have code that emits every second? How should we test that with RxJS marbles? And does our test then run also for more than a second? Then we can test it like that:

it('should emit every second', () => {
    testScheduler.run((helpers) => {
        const { expectObservable } = helpers;

        const numbers$ = interval(1000).pipe(take(5));

        expectObservable(numbers$).toBe(
            '1s 0 999ms 1 999ms 2 999ms 3 999ms (4|)',
            {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4});
    });
});

What’s going on in this test?

  • First, tests running with the testScheduler, i.e. inside the run method are using virtual time. Virtual time does not correspond to actual time and it can be used in the marbles diagram.
  • Usually, we’re using the dash in the marble diagram. This dash stands for a virtual time frame of 10ms. But similarly we can also define other time frames by using ms and s.
  • 1s: The interval operator emits after 1000 ms. Hence we are starting our marble diagram with 1s.
  • 0 999ms 1
    • After one second a first value indicated by ‘0’ is emitted
    • Numbers in the marble diagram are interpreted as string. That’s why we are mapping ‘0’ to 0 as actual output value
    • The emission of a value takes 1ms. After ‘1s 0’ we are at 1001ms. Hence, we only need to move 999ms forward to get to the time frame 2s (2000ms). And after that the value 1 is emitted at virtual time 2001ms.

The runtime of above test is 6ms. So, thanks to virtual time we don’t need to wait multiple seconds for the test to finish.

Exercise for the interval operator 💪

Use interval to implement polling. Stop polling as soon as you get a certain response.