RxJS Long Polling with a Retry Count

November 24, 2021 | 1 min to read

pipe image

When you need to continuously get some data from the server one of the options is the Long Polling technique. But when it comes to the implementation it’s very easy to get frustrated. Likely RxJS has some operators which can simplify the job for us and the solution for the problem will take only a few lines.

Imagine that we have the following method which fetches some data from a server:

requestData(): Observable<HttpResponse<...>> { return this.httpClient.get(...); }

We would like to request data at a specific interval. Additionally, if the server responds with an error a few times in a row, we would like to break the polling process (e.g server is down). Here comes the solution:

startPolling(requestInterval = 1000, retryCount = 5) { timer(0, requestInterval) .pipe( concatMap(() => this.requestData()), retryWhen((error$) => error$.pipe( delay(requestInterval), take(retryCount)) ), ) .subscribe((res) => ...); }

The startPolling method is going to do polling every 1000 ms. If the process encounters a failed request, then it will try to repeat the request 5 times and if there’s won’t be any successful response, the polling process will be completed.

Now let’s take the RxJS flow apart.

timer(0, requestInterval) – first, we initiate the interval using timer . The timer operator will emit a value every requestInterval (in our example it is 1000ms).

concatMap(() => this.requestData()) – after we get a value from the timer we need to map the value into request. For that, we use concatMap. It works the same as the mergeMap operator but with one critical difference. It will not subscribe to the next observable until the previous one will be completed. It will help us to keep a strict order of our requests. So the situation when the previous request gets finished after the next one is not possible.

retryWhen((error$) => error$.pipe(... – if we encounter an error within our polling process, then using the retryWhen operator we try to send the same request again (it will send the same parameters and metadata as well). It will do the retry with a delay of requestInterval. And to limit the number of attempts we use the take operator, which will take only a certain amount of failed requests (in our case 5). After that, the polling process will be completed.

If you need to provide some additional error handling, you can do that within error$.pipe.

You should NOT use Store in your web applications