RxJS ‘repeat’ operator — beginner necromancer guide

Or how to enliven dead Observables…and why

RxJS ‘repeat’ operator — beginner necromancer guide

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.

Given:

  1. On our webpage, we want to track mouse hold and mouse dragging events.
  2. 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.
  3. If the mouse is held less then 2 seconds and starts moving — we want to emit mousemove event values from a mouse_drag$ observable.

Implementation

OK, let's start by creating the 3 main observables we need for our solution that will produce the corresponding mouse event values:

Composing mouse_hold$

OK, now we need to compose mouse_hold$ according to this algorithm:

  1. When mouse_Down$ emits a value we should start a 2000ms timer
  2. If the timer emits value in 2000ms— we should emit a string: HOLD
  3. If mouse_Up$ or mouse_Move$ emit a value before the 2000ms timer emission — we should complete mouse_hold$

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

snippet link

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_Up$ or 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_Up$ or mouse_Move$ ) and the takeUntil operator — to complete mouse_Hold$ if any of them emits.

snippet link

Composing mouse_drags$

OK, now we need to compose mouse_drags$ according to the following algorithm:

  1. When mouse_Down$ emits a value, we start waiting for both the mouse_hold$ and mouse_move$ observables.
  2. If mouse_move$ emits (before mouse_hold$ emits)— we should continue re-emitting the mousemove event object (our main goal actually).
  3. If mouse_Up$ or 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.

Any observable sequences emit only once and then completes

But why?!!!

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!

Photo by Paige Cody on Unsplash

Unfortunately no, that didn't help…

OK, then the last method — I should put a call into the main RxJS necromancer:

The RxJS main necromancer gives advice for beginners.

So, all we need to do — is to use the repeat s̵p̵e̵l̵l̵ operator from the main spellbook.

snippet link

Let's run it in a codepen — and now this works as expected!

Now observable sequences are restarted!

How does ‘repeat’ work?

Let's take a look at how it is implemented in the RxJS GitHub repo:

snippet link

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 source$ again.

Conclusion

  1. repeat can help with making dead observables alive again.
  2. You can read more about the repeat logic in RxJS in the article ‘retry vs repeat’.
  3. Take a look at an interesting video “RxJS By Example” of RxJS Core team lead — Ben Lesh
  4. 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!