Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing rapid clicks on a button and making a request using rxjava

I have the following method that makes a request to get a pokemon from a endpoint.

I would like to prevent the user in making rapid requests by clicking quickly on the button that will invoke this method many times. I have used the throttle* methods and debounce.

Bascially, what I am looking for if the user rapidly clicks on the button within 300 milliseconds duration it should accept the last click in that duration. However, what i am experiencing is that all requests are being made. i.e. if the user rapidly clicks 3x within that duration I still get 3 requests.

   fun getPokemonDetailByName(name: String) {
        pokemonDetailInteractor.getPokemonDetailByName(name)
            .subscribeOn(pokemonSchedulers.background())
            .observeOn(pokemonSchedulers.ui())
            .toObservable()
            .throttleFirst(300, TimeUnit.MILLISECONDS)
            .singleOrError()
            .subscribeBy(
                onSuccess = { pokemon ->
                    pokemonDetailLiveData.value = pokemon
                },
                onError = {
                    Timber.e(TAG, it.localizedMessage)
                }
            ).addTo(compositeDisposable)
    }
like image 867
ant2009 Avatar asked Jul 09 '20 12:07

ant2009


3 Answers

Bascially, what I am looking for if the user rapidly clicks on the button within 300 milliseconds duration it should accept the last click in that duration

to me sounds more like that debounce operator behaviour. From the documentation

Debounce — only emit an item from an Observable if a particular timespan has passed without it emitting another item

you can see the marble diagram here

like image 75
Blackbelt Avatar answered Nov 18 '22 13:11

Blackbelt


private val subject = PublishSubject.create<String>()

init {
    processClick()
}

fun onClick(name: String) {
    subject.onNext(name)
}

private fun processClick() {
    subject
        .debounce(300, TimeUnit.MILLISECONDS)
        .switchMap { getPokemonDetailByName(it) }
        .subscribe(
            { pokemonDetailLiveData.value = it },
            { Timber.e(TAG, it.localizedMessage) }
        )
}

private fun getPokemonDetailByName(name: String): Observable<Pokemon> =   
     pokemonDetailInteractor
        .getPokemonDetailByName(name)
        .subscribeOn(pokemonSchedulers.background())
        .observeOn(pokemonSchedulers.ui())
        .toObservable()

In your case, the getPokemonDetailByName creates a new subscription every time. Instead, send the click events to a Subject, create a single subscription to that stream and apply debounce.

like image 5
ckunder Avatar answered Nov 18 '22 12:11

ckunder


The getPokemonDetailByName() subscribes to a new stream every time it is called.

Instead of subscribing to a new stream every time, just provide a subject to send the data to and map it directly to a LiveData with LiveDataReactiveStreams.fromPublisher().

private val nameSubject = PublishSubject.create<String>()

val pokemonDetailLiveData = nameSubject.distinctUntilChanged()
                .observeOn(pokemonSchedulers.background())
                .switchMap(pokemonDetailInteractor::getPokemonDetailByName)
                .doOnError { Timber.e(TAG, it.localizedMessage) }
                .onErrorResumeNext(Observable.empty())
                .toFlowable(BackpressureStrategy.LATEST)
                .to(LiveDataReactiveStreams::fromPublisher)

fun getPokemonDetailByName(name: String) {
    nameSubject.onNext(name)
}

The observeOn(pokemonSchedulers.background()) operator is needed as subjects handle subscriptions differently. The onErrorResumeNext(Observable.empty()) ensures that only valid objects end up in the LiveData.

Like this only a single stream is subscribed to once the pokemonDetailLiveData is being observed. The PublishSubject ensures that only a user click triggers the update from the API and only a single API call is active at the same time.

like image 3
tynn Avatar answered Nov 18 '22 12:11

tynn