33

Use case: Call a function every minute (60000 ms) that dispatches store action to fetch lastUpdated status of items, which upon response and filtering, updates the store, and updated store is read as an observable and displayed in the view). This needs to happen for as long as the web app is open (so indefinitely).

Currently, I'm using this:

this.refreshDate = window.setInterval(
  () => this.store.dispatch(new FetchLastUpdate())
, 60000);

And when view is destroyed/dismounted, I delete the interval as so:

if (this.refreshDate) {
  clearInterval(this.refreshDate);
}

Is this efficient/effective, or is it troublesome?

Why would I want to use an RxJS polling strategy like:

interval(60000)
  .pipe(
    startWith(0),
    switchMap(() => this.store.dispatch(new FetchLastUpdate()))
   );

Or

timer(0, 60000)
  .pipe(
    switchMap(() => this.store.dispatch(new FetchLastUpdate()))
  );

TL;DR: window.setInterval() vs. RxJS timer()/interval()


Conclusion/answers (for ease of research):

There is great benefit to using RxJS functions to set an interval or perform polling, these benefits are explained in the selected answer but also in comments, but it is concluded (by discussions in the comments) that for the very simple requirement defined in the "Use case" section at the beginning of this post, it is unnecessary to use RxJS, and in fact if you are not using RxJS in any other part of your program, do not import it just for this, however in my case, I had already imported and used RxJS elsewhere.

msamprz
  • 573
  • 2
  • 7
  • 15
  • 1
    I think the `FetchLastUpdate` will not last more than `60` seconds, so using `switchMap` seems to be overkill, I would rather use `setInterval` which seems to be enough for your use case – Olivier Boissé Aug 25 '18 at 16:28
  • 1
    @OlivierBoissé right, that's what I've been presuming and going with, but I thought I'd seek other more experienced opinions. And as for the switchMap, you're right, tap would probably be better. – msamprz Aug 25 '18 at 16:43
  • 1
    moreover adding `rxjs` will increase your js bundle size, so if you don't need it somewhere else you can skip it and use `setInterval` – Olivier Boissé Aug 25 '18 at 17:01
  • 1
    @OlivierBoissé that's true, though for the reason you mentioned (using it elsewhere), it's not applicable to me. Thanks :) – msamprz Aug 25 '18 at 17:04

2 Answers2

38

Advantage of RxJS:

Laziness

You can create your Observables and until you call subscribe nothing is happening. Observable = pure function. This gives you more control, easier reasoning and allows for next point...

Composability

You can combine interval/timer with other operators creating custom logic very easily in unified way - for example you can map, repeat, retry, take... etc. see all operators

Error Handling

In case of an error you are responsible for calling clearTimeout/clearInterval - Observables are handling this for you. Resulting in cleaner code and fewer memory leak bugs.

Of course anything you do with Observables you can also do without Observables - but that's not the point. Observables are here to make your life easier.


Also note that interval/timer are not good observable factories for polling because they do not "wait" for your async action to finish (you can end up with multiple async calls running over each other). For that I tend to use defer and repeatWhen like this:

defer(() => doAsyncAction())
  .pipe(
    repeatWhen(notifications => notifications.pipe(delay(1234)))
  );
m1ch4ls
  • 3,317
  • 18
  • 31
  • 1
    Thank you for your info-packed answer, these are now definitely things I will take into consideration the next time I'm polling or something of the sort, so your answer helped with the general idea of the benefit of `interval()`/`timer()` (or `defer()`) vs pure JS, but considering the use-case I defined in the post itself, I don't see any benefits to be gained for this type of use. – msamprz Aug 25 '18 at 16:50
  • 1
    If you don't have other use for rxjs then of course - use `window.setInterval` but make sure your `FetchLastUpdate` action doesn't take more then 60s :) – m1ch4ls Aug 25 '18 at 16:53
  • 1
    Also, to clarify why I don't see any benefits for this use-case, I made another comment: There's no need to create a whole (clean) logic if the requirement is to just to call a single function every minute. I wouldn't need any error handling as my `FetchLastUpdate()` action handles all of its own errors, the store handles its own errors, as well as the async HTTP calls. The auto-cleanup is beneficial though, so that I won't have to call `clearInterval()`, but I'm not sure if that's worth the overhead of calling RxJS functions just to avoid that. Although I may be overestimating this. – msamprz Aug 25 '18 at 17:00
  • (This is the reply to your comment reply) Okay, that's good closure. @OlivierBoissé supported that statement too. Thank you :) – msamprz Aug 25 '18 at 17:02
  • 1
    I have selected your answer as the answer of the post due to the clear information layout and that it speaks for the general use of such RxJS strategies instead of pure JS. @OlivierBoissé had his answer as the finalizing answer of this use-case, but he hadn't made an answer post, just a comment on the post itself. – msamprz Aug 26 '18 at 10:41
8

window.setInterval doesn't care about your callbacks state, it'll execute at the given interval despite the status of the execution of the past callback, and the only way to make it stop and skip is clear the interval or reinitialize it.

On the other hand, RxJS Observable based solutions(interval, timer) allow you to pipe conditional operators (takeWhile, skipWhile for example) which allows you to add a stop or implement a stop-start logic by just flipping a boolean flag, instead of adding complicated logic of clearing the interval, and then recreating it.

And they are observables, you can listen to them all across the application, and attach any number of listeners to it.

Error Handling is better too, you subscribe to all successes, and handle everything in a catch callback.

DesTroy
  • 390
  • 4
  • 17
  • Wrong! if `window.setInterval` has a callback which is XHR based, you can make it wait with using async/await. Ramda and other libraries are just an overkill. – Adeel Imran Aug 25 '18 at 16:15
  • 2
    @Adeel do you have an example code to do what you suggest? As I understand it is not possible to prevent setInterval for running the callback again even before the last one finished? – Chris Cousins Aug 25 '18 at 16:30
  • Thank you for the response. I hadn't seen your post before @m1ch4ls's post, to which I commented on. But basically, you're right, and I'll definitely consider these points the next time I'm polling or whatnot because I hadn't thought of them before. It's very advantageous to have easy and clean access to those operators, and the more flexible error-handling. Other cases/people who read this will benefit from these points however I don't see how these would benefit my specific case. I'll paste the reasons from my other comment in the next comment here. – msamprz Aug 25 '18 at 17:13
  • Also, to clarify why I don't see any benefits for this use-case, I made another comment: There's no need to create a whole (clean) logic if the requirement is to just to call a single function every minute. I wouldn't need any error handling as my `FetchLastUpdate()` action handles all of its own errors, the store handles its own errors, as well as the async HTTP calls. The auto-cleanup is beneficial though, so that I won't have to call `clearInterval()`, but I'm not sure if that's worth the overhead of calling RxJS functions just to avoid that. Although I may be overestimating this. – msamprz Aug 25 '18 at 17:14
  • 1
    @Chris Here is a quick example I cooked up https://codesandbox.io/s/q80j06y516, note where I am fetching a random user in this example. I can wrap it in a try/catch and clear my interval and as well perform error handling as well. Hope this helps. – Adeel Imran Aug 25 '18 at 17:34