1

I am creating an Angular application that will poll a backend API at regular intervals. I would like to keep the data on the page up-to-date with the information on the backend, in case it changes independently of a user's action in the front end.

When the user completes an action on the page, it will kick off the polling. Initially, the action returns an object with the new state of the item. However, soon after completing that action, the object's status may change on the backend. Over time, the chance that the object's status will change without user interaction drops.

I would like to implement polling that uses an increasing timeout for each subsequent call, to model the expected behavior of the object and reduce strain on the backend resource.

For example:

  • Initial call, wait 1 second
  • Repeat call, wait 2 seconds
  • Repeat call, wait 4 seconds
  • Repeat call, wait 8 seconds
  • Repeat call, wait 16 seconds

I have seen articles that describe how to set up polling with Observables in RxJS, but they all seem to use a static timeout (i.e. always wait 5 seconds between calls).

How can I repeat an HTTP call within Angular at regular, increasing intervals?

Scott Kearney
  • 173
  • 10
  • create a local variable and set with increasing interval – display name Nov 21 '17 at 16:14
  • so if i drop a variable into the timeout function, it will re-read it on each execution? – Scott Kearney Nov 21 '17 at 16:17
  • You might want to consider using Web Sockets (https://stackoverflow.com/questions/10028770/in-what-situations-would-ajax-long-short-polling-be-preferred-over-html5-websock?noredirect=1&lq=1) – Ankit Nov 21 '17 at 16:32
  • Thanks for the suggestion, @Ankit! WebSockets are a little bit outside the scope of my project right now, but I will definitely keep that in mind for the future. – Scott Kearney Nov 21 '17 at 16:43

2 Answers2

4

While I'm sure there is always some better way - for the sake of readability of the code I'd go with something like the following.

function callWithIncreasingInterval() {
  var count = 1;
  return Rx.Observable.of(true)
      .delayWhen(() => Rx.Observable.timer(Math.pow(2, count)))
      .switchMap(() => someRestCall())
      .do(() => ++count)
      .repeat();
}

function someRestCall() {
    return Rx.Observable.of("some rnd " + Math.random()).delay(50);
}

callWithIncreasingInterval()
  .subscribe(result => console.log(result));
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

But as a general note: For an issue like this one should really consider websockets, it's 2017 ;-)

olsn
  • 16,644
  • 6
  • 59
  • 65
  • 1
    Thank you for your help! For those angular & rxjs newbies like me, don't forget to include `import 'rxjs/Rx';` at the top of the file, otherwise `.delayWhen` won't be found. – Scott Kearney Nov 21 '17 at 18:59
  • I promise to look into converting this to WebSockets as soon as I get the rest of this application working! :-) – Scott Kearney Nov 21 '17 at 19:03
  • See also: https://stackoverflow.com/questions/37458208/angular-2-how-to-change-the-interval-of-an-rxjs-observable – Scott Kearney Nov 21 '17 at 19:38
1

@olsn solution is pretty okay, but here is a recursive one. It is a bit more generic as well.

Btw. be aware of exponential growth of your delay - probably you do not want to let it increase without any bound.

const repeatableAction = (action, when = 1000, nextWhen = current => current * 2) =>
  Rx.Observable.timer(when)
  .mergeMapTo(action
    .concat(Rx.Observable.defer(() =>
      repeatableAction(action, nextWhen(when), nextWhen)))
  )

let start = Date.now()
repeatableAction(Rx.Observable.of('ACTION'))
  .do(x => console.log(x, (Date.now() - start) / 1000))
  .subscribe()
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
artur grzesiak
  • 20,230
  • 5
  • 46
  • 56
  • 1
    I like this approach as well, as I already mentioned: there are definitely "cleaner/better" solutions than mine, but from previous projects one of my biggest takeaways from RxJS is, that noone understands it (except us geeks) - so when working with an average team, you are better of with less complex streams, even if they are not as "elegant". - But aside from the complexity, I do like your solution. – olsn Nov 22 '17 at 07:53