Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxSwift - Debounce/Throttle "inverse"

Tags:

Let's say I have an instant messaging app that plays a beep sound every time a message arrives. I want to debounce the beeps, but I'd like to play the beep sound for the first message arrived and not for the following ones (in a timespan of, say, 2 seconds).

Another example might be: my app sends typing notifications (so the user I'm chatting with can see that I'm typing a message). I want to send a typing notification when I start typing, but only send new ones in X-seconds intervals, so I don't send a typing notification for every character I type.

Does this make sense? Is there an operator for that? Could it be achieved with the existing operators?

This is my code for the first example. I'm solving it now with debounce, but it's not ideal. If I receive 1000 messages in intervals of 1 second, it won't play the sound until the last message arrives (I'd like to play the sound on the first one).

self.messagesHandler.messages             .asObservable()             .skip(1)             .debounce(2, scheduler: MainScheduler.instance)             .subscribeNext { [weak self] message in                     self?.playMessageArrivedSound()             }.addDisposableTo(self.disposeBag) 

Thanks!

like image 633
Bruno Koga Avatar asked Feb 16 '16 16:02

Bruno Koga


People also ask

What is Rxswift throttle?

The throttle only emits the last item in a particular timespan. It's useful when you want to filter multiple events like tapping on the button. Only emit the last tap event in a particular timespan.

What is the difference between Debounce and throttle?

Debouncing is a technique where we can monitor the time delay of user action and once that delay reaches our predetermined threshold we can can make the function call. Throttling is a technique where we make the function call in a predetermined time interval irrespective of continuous user actions.


2 Answers

Updated for RxSwift 3 and improved throttle operator

With new behavior of throtlle operator, introduced in RxSwift 3.0.0-beta.1, you can use it just like that:

    downloadButton.rx.tap     .throttle(3, latest: false, scheduler: MainScheduler.instance)     .subscribe(onNext: { _ in         NSLog("tap")     }).addDisposableTo(bag) 

Old version of answer

Use window operator and then transform Observable<Observable<Type>> to flat Observable using flatMap.

This sample code prints 'tap' only for first tap in every 3 seconds windows (or if tap count exceeds 10000).

    downloadButton.rx_tap     .window(timeSpan: 3, count: 10000, scheduler: MainScheduler.instance)     .flatMap({ observable -> Observable<Void> in         return observable.take(1)     })     .subscribeNext { _ in         NSLog("tap")     }.addDisposableTo(bag) 
like image 177
Evgeny Sureev Avatar answered Oct 08 '22 01:10

Evgeny Sureev


Window is a great solution, but I find the sample operator more intuitive and also with correct behavior.

messagesHandler.messages                .sample(Observable<Int>.timer(0.0, period: 2.0, scheduler: MainScheduler.instance))                .subscribeNext { [weak self] message in                     self?.playMessageArrivedSound()                }.addDisposableTo(self.disposeBag) 

The throttle operation does not do what I thought it should.

For people that also find throttle is too confusing:

"throttle will only forward an event once the source observable has stopped sending events for the specified period of time. This does not work well with regular event delivery" for more details.

In this case, the filter you want is

sample(Observable<Int>.timer(0.0, period: 2.0, scheduler: MainScheduler.instance)) 
like image 20
Guy Kahlon Avatar answered Oct 08 '22 01:10

Guy Kahlon