Rxswift: Correct way for using throttle

Created on 6 Sep 2015  Â·  12Comments  Â·  Source: ReactiveX/RxSwift

Hi,

I'm new to Rx and wondered what is throttle is all about.

here is my code

 locationManager.rx_didUpdateLocations
            .throttle(2, MainScheduler.sharedInstance)
            .debug()
            .flatMap { location -> Observable<BaseResponseObject>  in
                self.mapView.camera = GMSCameraPosition(target: location.first!.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
                return LocationTrackingRequest.sharedInstance.updateRequest(location.first!, endPoint: "location")
            }
            .retry()
            .catchError { error in
                print("ERROR")
                return empty()
            }
            .subscribeNext { x  in
                print("Subscribe \(x)")
            }

From what i read, it should fired the map every 2 seconds. But i waited it didn't call the rest chain. So i kind getting lost there. So what did it do and how to do it?

question

Most helpful comment

Looks like we now have throttle(0.5, latest: false, scheduler: MainScheduler.instance) The latest boolean can be set to false if you don't wan't the last event to be fired

All 12 comments

Throttle doesn't emit any events itself in that sense, it only forwards them, in this case the events emitted by rx_didUpdateLocations. However, it forwards only one event per given time, in this case two.
So if rx_didUpdateLocations doesn't emit any events, throttle doesn't forward any either.

Check out the this link to visualise it a little more.

Now I'm wondering however what the difference between debounce and throttle is :p

Yes, thank you.

After reread the documentation, i found this one.

It would be great if program would wait for some time before firing that request to server, after all, we > > don't want to spam our servers in case somebody is in the process of fast typing something very long. > Additional timer field maybe?

And after that, i achieved what i want with filter command.

from my understanding debounce will wait for the time given, and then send all that event at once. While throttle will wait until no event received and then forward them.

CMIIW

@suprie

Throttle and debounce are aliases for the same thing. :)

http://reactivex.io/documentation/operators/debounce.html

Thanks @larcus94 for the extremely short but neat explanation.

If you have similar questions, please consider joining the dedicate Slack channel.

Hmm, throttle does not do what I thought it did. Looks like this is a common complaint across Rx implementations, someone misnamed it early on & now we're stuck with it.

For people finding this issue: 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; in the case of rx_didUpdateLocations, if you specify a duration longer than 1s, no location updates will ever be delivered. debounce is a much better name for this behaviour - IMO no-one should ever use throttle; it’s too confusing.

In this case, the filter you want is sample(Observable<Int>.timer(0.0, period: 2.0, scheduler: MainScheduler.instance))

FWIW I added a proposal for a new operator that handles that kind of situation, to be added to RxSwift+Ext: see description of the throttledSample operator, until we have a better name :)

@fipillet awesome! TBH though the sampling approach is STILL not the way a ‘proper’ throttle should work - if you have a long duration, you don’t get your initial event until the first sample, and there will be a delay introduced on the events that are delivered. Ideally:

  1. first event is automatically forwarded
  2. subsequent events within the time period are filtered
  3. next event received after that time period is forwarded
    etc.

Looks like ReactiveCocoa went through this a while ago and dropped the debounce style throttle in the Swift rewrite for the logic as described above. If it were up to me, I’d deprecate the existing throttle synonym - it can’t be easily replaced, but it could be removed.

@samritchie what you're asking for is exactly what I'm suggesting with throttledSample. Not sure I made it clear that the first event would immediately make it through, but this is the intent.

Also the issue with replacing throttle behavior is that RxSwift aims at compatibility with other Rx implementations, if only not to confuse people. I think keeping the current behavior, albeit flawed, is a good thing and creating a new operator that does the right thing is the way to go.

@fpillet the throttle that I was imagining wouldn’t require a scheduler - it would be pure filtering only. It’s possible dropping the 'last' events in a cluster would be unexpected, but I wouldn’t have thought so.

According to the doc linked above, the ONLY Rx implementation that uses throttle is Rx.NET - the rest all use debounce and/or something like throttleWithTimeout. Renaming to throttleWithTimeout would at least be less bad.

Hello, then, how Could I have a time that accumulates events and then send all of them instead of the last one?

Looks like we now have throttle(0.5, latest: false, scheduler: MainScheduler.instance) The latest boolean can be set to false if you don't wan't the last event to be fired

Was this page helpful?
0 / 5 - 0 ratings