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
.