Posted at

RxSwiftのDebounceとThrottle

More than 1 year has passed since last update.

Swift愛好会 Advent Calendar 2017の12日目担当の @dekatotoroです。

昨日は、 @marty-suzukiさんの 「Swift4からprotocolのassociatedtypeでできるようになったこと」 でした。

今日は「RxSwiftのDebounceとThrottle」についてです。

RxSwiftのoperatorにDebounceThrottleがありますが、

「あれ、どっちがどういう動作だったっけ?」と思ったことはありませんか。

忘れたときはこの記事を見てみてください :smiley:


パターンは3つあります。


  • Debounce

  • Throttle (latest true)

  • Throttle (latest false)

以下のストリームをを例にします。

※ 確認したversionは RxSwift 4.0.0

let observable = Observable<Int>.create { observer in

observer.onNext(1)
observer.onNext(2)
observer.onNext(3)

sleep(2)

observer.onNext(4)
observer.onNext(5)
observer.onNext(6)

sleep(2)

observer.onCompleted()
return Disposables.create()
}

stream.png


Debounce

指定期間新たなイベントが発行されなくなってから、最後に発行されたイベントを発行します。途中のイベントは無視されます。

_ = observable

.debounce(1, scheduler: scheduler) // 3, 6
.subscribe(onNext: {
print("debounce: \($0)")
})

図にしてみます。

debounce.png


Throttle

RxSwift 3.4.0からlatestパラメーターが追加されています。

defaultはtrueです。

計測が開始された最初のイベントと、指定期間内で最後に発行されたイベント(最初のイベント以外)を発行します。途中のイベントは無視します。

latestがfalseの場合は、計測が開始された最初のイベントを発行し、指定期間内のイベントは全て無視します。

debounceとの違いは、指定期間の計測が開始された最初のイベントを発行すること、latestがtrueの場合は指定期間内で発行されたイベント(最初のイベント以外)が指定期間経つと必ず発行するところです。


Throttle (latest true)

_ = observable

.throttle(1, latest: true, scheduler: scheduler) // 1, 3, 4, 6
.subscribe(onNext: {
print("throttle true: \($0)")
})

図にしてみます。

throttle_true.png


Throttle (latest false)

_ = observable

.throttle(1, latest: false, scheduler: scheduler) // 1, 4
.subscribe(onNext: {
print("throttle false: \($0)")
})

図にしてみます。

throttle_false.png


Example

実際に使用する例です。

debounceはインクリメンタルサーチなどでAPIを叩き過ぎないようにするのに便利ではないでしょうか。

throttle latest falseはボタン連打防止など色々用途がありそうです。

throttle latest trueはあまり使い所が思い浮かばないなので、使ってる方は使用例ご教授ください :pray:


class ViewController: UIViewController {

@IBOutlet private weak var searchBar: UISearchBar!
@IBOutlet private weak var button: UIButton!
private let disposeBag = DisposeBag()

override func viewDidLoad() {
super.viewDidLoad()

searchBar.rx.text
.debounce(0.3, scheduler: MainScheduler.instance)
.subscribe(onNext: { text in
print("UISearchBar debounce: \(text ?? "") \(Date())")
})
.disposed(by: disposeBag)

button.rx.tap.asObservable()
.throttle(1.0, latest: false, scheduler: MainScheduler.instance)
.subscribe(onNext: { _ in
print("UIButton throttle latest false: \(Date())")
})
.disposed(by: disposeBag)
}
}

以上になります :smile:

Swift愛好会 Advent Calendar 2017 明日は @mizukoA になります!