rxjs

rxjs poll for data on timer and reset timerwhen manually refreshed


I am using the following libraries in the relevant application: Angular 4.x, ngrx 4.x, rxjs 5.4.x

I have an api that I need to poll every 5 minutes. The user is also able to manually refresh the data. That data is stored in an ngrx store. I am using ngrx effects so the data is retrieved by dispatching an action of type 'FETCH'.

I want to setup a rxjs stream where it will dispatch the 'FETCH' action to the ngrx store. It will be a sliding 5 minute timer that resets when the user manually updates the store. The stream should initially emit a value when subscribed.

I'm not sure how I can reset the timer. In plain javascript I would do something like the following:

console.clear();
let timer;
let counter = 0;

function fetch() {
  console.log('fetch', counter++);
  poll();
}

function poll() {
  if (timer != null) {
    window.clearTimeout(timer);
  }
  timer = window.setTimeout(() => {
    console.log('poll');
    fetch();
  }, 5000);
}

function manualGet() {
  console.log('manual');
  fetch();
}

fetch();
<button onClick="manualGet()">Get Data</button>

Question: How do I emit on an interval that is reset when another stream emits like the example again?


Solution

  • You want two components to your stream – a timer and some user input. So let's start with the user input. I'll assume some button which can be clicked:

    const userInput$ = Observable.fromEvent(button, 'click');
    

    Now we want to start a timer which resets everytime userInput$ emits. We can do that using

    userInput$.switchMap(() => Observable.timer(0, 5000));
    

    However, we also want this stream to start without the user having to first click the button. But that's also not a problem:

    userInput$.startWith(null);
    

    Now we put it all together:

    Observable.fromEvent(button, 'click')
        .startWith(null)
        .switchMap(() => Observable.timer(0, 5000))
        .subscribe(() => dispatchFetch());
    

    Note that I am following your examples of using a 5 second timer, not a 5 minute timer (which you mentioned in the question.)