RxJS ‘repeat’ operator — beginner necromancer guide
Or how to enliven dead Observables…and why
As you may know the RxJS repeat operator restarts a source Observable and applies it a specific number of times. You can read more about it in my article ‘retry vs repeat’.
In other words repeat makes dead (complete) Observables alive again. Ok, but why should I care? Keep reading to find out:-)
Let's consider a learning task that will help us to get deeper in understanding when should we use this necromancer tool.
- On our webpage, we want to track mouse hold and mouse dragging events.
- If the mouse is held more then 2 seconds and doesn’t move — we want to emit the string ‘HOLD’ from a mouse_hold$ observable.
- If the mouse is held less then 2 seconds and starts moving — we want to emit mousemove event values from a mouse_drag$ observable.
OK, let's start by creating the 3 main observables we need for our solution that will produce the corresponding mouse event values:
OK, now we need to compose
mouse_hold$ according to this algorithm:
mouse_Down$emits a value we should start a 2000ms timer
- If the timer emits value in 2000ms— we should emit a string: HOLD
mouse_Move$emit a value before the 2000ms timer emission — we should complete
For step 1 we will use the RxJS timer function. Usually, it is used for periodic value emissions, but if it is provided with one argument (inactivity duration) — then it emits value (zero) one time only.
const timer$ = timer(2000);
const timer$ = timer(2000);
So for step 2 we should use the switchMap operator to return an Observable that will emit a string: HOLD
Here we make a switch two times: when
mouse_Down$ emits — we switch to the
timer$ Observable. In 2000ms
timer$ will emit and we will switch to an
of('HOLD') Observable. So at the end, the
mouse_Hold$ subscribers will get a HOLD string on a 2000ms period.
Now we will implement step 3 — if one of
mouse_Move$ emits a value — we should complete the observable. We can reach this by using the merge function (to combine values from both
mouse_Move$ ) and the takeUntil operator — to complete
mouse_Hold$ if any of them emits.
OK, now we need to compose mouse_drags$ according to the following algorithm:
mouse_Down$emits a value, we start waiting for both the
mouse_hold$emits)— we should continue re-emitting the mousemove event object (our main goal actually).
mouse_hold$emit a value — we should complete mouse_drags$.
As in the previous example for step 1 we will use switchMap.
For step 2 we will use switchMap as in a previous example.
mouse_drags$ = mouse_Down$.pipe( switchMap((time) => mouse_Move$) )
For step 4 we will use takeUntil and merge as we did in a previous example:
mouse_drags$ = mouse_Down$.pipe( switchMap((time) => mouse_Move$, takeUntil(merge(mouse_Up$, mouse_Hold$)), )
Seems like it is done, isn’t it?
Packtpub.com and I prepared a whole RxJS course with many other details of how you can solve your every-day developer’s tasks with this amazing library. It can be interesting for beginners but also contains advanced topics. Take a look!
And what does the necromancy?
Now let's try to subscribe to the created observables (mouse_drags$ and mouse_hold$) and run this code (you can find out why we should subscribe to run it in my other article).
Well, if you try to run this code in a codepen (Run and then just press a mouse button for more then 2 seconds), it will work only once.
Because the takeUntil operator completes the sequences. And we should restart these dead observables to make them wait for the mousedown event again.
First I want to try some magic words!
Unfortunately no, that didn't help…
OK, then the last method — I should put a call into the main RxJS necromancer:
So, all we need to do — is to use the repeat s̵p̵e̵l̵l̵ operator from the main spellbook.
Let's run it in a codepen — and now this works as expected!
How does ‘repeat’ work?
Let's take a look at how it is implemented in the RxJS GitHub repo:
Aha, these spells are really not so hard to understand:-) We count the number times the
source$ observable runs. If it is more then 0 — we subscribe to
- repeat can help with making dead observables alive again.
- You can read more about the repeat logic in RxJS in the article ‘retry vs repeat’.
- Take a look at an interesting video “RxJS By Example” of RxJS Core team lead — Ben Lesh
- Live fast, code clean!
Like this article? Let's keep in touch on Twitter.
Starting from section 4 of my RxJS video course advances staff is reviewed — so if you familiar with RxJS already — you can find something useful for you as well: higher-order observables, anti-patterns, schedulers, unit testing, etc! Give it a try!
Special thanks to Alex Okrushko and Nicholas Jamieson for reviewing my article!