The RxJS timer operator waits a specified time or until an exact date before emitting. This allows you to delay operations.
RxJS timer explained 🎓
The timer operator has two main variants because it support an intervalOrScheduler as second argument. Anyway, it always emits numbers.
timer(dueTime: number | Date = 0,
intervalOrScheduler?: number | SchedulerLike,
scheduler: SchedulerLike = asyncScheduler
): Observable<number>
Delay the emission
The first variant lets you specify a due parameter. This can either be a number or a Date. In the case of number the operator wait the specified time in milliseconds until it emits. On the other hand if a Date is passed the emission does not happen until that date. In both cases this variant emits the number 0.
timer(due: number | Date, scheduler?: SchedulerLike): Observable<0>
This means that in the following code 0 is emitted after 10s:
timer(10 * 1000).subscribe({ next: console.log });
// 0
The same result can be achieved by specifying a date:
const currentDate = new Date();
const tenSecondsFromNow = new Date(
currentDate.getFullYear(),
currentDate.getMonth(),
currentDate.getDate(),
currentDate.getHours(),
currentDate.getMinutes(),
currentDate.getSeconds() + 10
);
timer(tenSecondsFromNow).subscribe({ next: console.log });
// 0
Start an interval
A second variant allows to start an interval after some delay. This function is defined as follows:
timer(startDue: number | Date, intervalDuration: number, scheduler?: SchedulerLike): Observable<number>
That means if you want to start an interval after 500ms with values emitted every 100ms you would specify:
timer(500, 100);
The emission in this case starts at 0 but it does not end there. The value is increased by 1 on each emission. Hence, the following code emits the numbers 0 to 30.
it('should start an interval after 500ms', (done) => {
timer(500, 100).subscribe({ next: (x) => {
console.log(x);
if (x === 30) {
done();
}
}
});
});
What problems does the RxJS timer operator solve? 🚧
First, the timer operator offers one way to delay an emission or operation. When specifying a date you can not only wait a fixed amount of milliseconds but also until a specific point in time.
If you remember the interval operator you know that in that case the emission starts after the specified time. The emission for interval(1000) only starts after 1s. In some cases this is not ideal and you want to immediately start the emission, e.g. for polling. This is where the timer() operator can help:
timer(0, 1000).subscribe(n => console.log('timer', n));
// starts immediately
interval(1000).subscribe(n => console.log('interval', n));
// starts only after 1s
So, you can start the polling already immediately. A good example of http polling can be found here.
How to test the timer operator🚦
RxJS marbles is also here the way to go:
it('should emit after 200ms', () => {
testScheduler.run((helpers) => {
const { expectObservable } = helpers;
expectObservable(timer(200)).toBe('200ms (0|)', { '0': 0 });
});
});
The timer(200) in above code creates an Observable that emits after 200ms the value 0. At the same time it also completes.
Similarly also the interval variant can be tested:
it('should emit every 100ms and immediately', () => {
testScheduler.run((helpers) => {
const { expectObservable } = helpers;
expectObservable(timer(0, 100)
.pipe(take(3))
).toBe('0 99ms 1 99ms (2|)', { '0': 0, '1': 1, '2': 2 });
});
});
There is a next notification after which we wait 99ms. Then the next value is sent. The take(3) operator is passed in pipe to stop the emission after 3 values. At the same time also the Observable completes.
Exercise for the timer operator 💪
Use the timer() operator to start a countdown from 10 to 0. Of course you can use timer() together with map() and take() to get to a solution.
As always the code examples and exercise solution can be found on GitHub.