How to avoid duplicate network calls in an Rx stream?

Viewed 1156

I have the following stream.

Observable.just(Unit) // execute immediately
    .mergeWith(tryAgainRelay) // execute again when this relay emits
    .flatMap {
      api.call() // emits sealed class single containing `Success` or `Error` state
          .toObservable()
          .startWith(Loading) // emit loading (same sealed class as above)
    }

However, I never want duplicate (simultaneous) network calls (inner flatmap). If the tryAgainRelay emits while a network call is already in flight, I want this emissions to be discarded. I can avoid them with the following code:

var isLoading = false // track whether api call is in flight
Observable.just(Unit)
    .mergeWith(tryAgainRelay)
    .filter { isLoading.not() } // prevent emissions if api call is already in flight
    .flatMap {
      api.call()
          .toObservable()
          .startWith(Loading)
          // update state variable
          .doOnNext {
            isLoading = when (it) {
              Loading -> true
              is Error, is Success -> false
            }
          }
        }

It's a bit ugly, and involves stepping outside the stream with a .doOnNext(). Is there a better, more idiomatic way to do this?

1 Answers

Use concatMap instead of flatMap.

The retries will be created sequentially "queued" one after the other, so you can be sure that only one request will be executed in parallel (the next request will start when the previous one finishes - complete or error - and if tryAgainRelay has emitted).

Related