RxSwift Operator 살펴보기(Debounce, Throttle, Buffer)

2021. 4. 17. 19:47iOS/RxSwift

오늘은 RxSwift 다양한 Operator들을 하나씩 살펴보는 시간을 가져보도록 하겠습니다.

Debounce

먼저 알아볼 것은 Debounce 입니다. Debounce는 연속적인 이벤트가 발생했을 때 특정 시간 범위 동안 다른 이벤트가 발생되지 않는다면, 가장 마지막에 발생한 이벤트만 emit되고 나머지 연속적인 이벤트를 block 해주는 역할을 하는 operator 입니다

위의 marble 그림으로 부터 이해해 봅시다.

기존의 시퀀스에서 1이라는 이벤트가 처음으로 발생합니다. 이 이벤트는 그대로 emit이 되게 됩니다. 하지만 그 이후에 2, 3, 4, 5라는 이벤트가 좁은 간격으로 발생하는 것을 볼 수 있습니다. 하지만, 실제 debounce operator로 filter 한 결과는 5 라는 이벤트 입니다.

즉, 이벤트가 발생한 직후 다른 이벤트가 debounce를 통해 지정한 특정 시간 동안 들어오지 않게 된다면, 그대로 이벤트를 emit하게 됩니다. 하지만 연속적으로 계속해서 event가 발생한다면 이러한 event를 filter하게 됩니다.

아래 예제들을 보며 이해해 봅시다.

화면 위의 텍스트 필드 하나가 있습니다.

        txtField.rx.text.orEmpty.debounce(.seconds(1), scheduler: MainScheduler.asyncInstance)
            .subscribe(onNext: { text in
                print(text)
            })
            .disposed(by: disposeBag)
// text : 텍스트필드의 텍스트가 변경될 때 마다 Optinal<String>이벤트가 발생
// orEmpty : 위의 text를 통해 받는 이벤트에 대해 optinal unwrapping처리를 도와주는 operator

위의 코드처럼 .debounce를 통해 지정해 둔 시간은 1초 입니다.

앞서 설명드린 것 처럼, 이벤트가 발생한 직후 1초동안 어떠한 다른 이벤트가 발생하지 않는다면 그대로 emit이 되고, 만약 연속적으로 이벤트가 발생한다면 block해주는 역할을 합니다.

Throttle

throttle은 앞에서 설명 드렸던 Debounce와 비슷하면서도 다른 동작을 수행하기 때문에 헷갈려 하실 수 있습니다.

public func throttle(_ dueTime: RxSwift.RxTimeInterval, latest: Bool = true, scheduler: RxSwift.SchedulerType) -> RxSwift.Observable<Self.Element>

이 throttle은 이벤트가 발생하는 최소 간격을 제한할 수 있는 Operator입니다. 앞 서 debounce가 연속적인 이벤트를 block해 최소한의 이벤트를 emit할 수 있던 것과 달리, throttle을 통해 어떠한 이벤트가 emit되는 시점에서 지정한 시간동안 들어오는 이벤트들을 block함으로써, 이벤트가 emit되는 간격을 제한할 수 있습니다.

2초 간격으로 이벤트를 발생시키는 시퀀스가 존재한다고 생각해 봅시다.

이를 만약 5초를 지정한 throttle operator를 사용한다 한다면, '1'이라는 이벤트가 발생한 시점으로 부터 5초동안 들어오는 이벤트들을 block 할 수 있게 됩니다.

그리고 여기서 throttle을 사용하는 방향은 두 가지로 나뉘게 됩니다. 이벤트가 발생한 후 throttle을 통해 지정한 5초가 지난 이후에 어떻게 동작할 지를 , throttle의 두번째 parameter인 'lastest'를 통해 정할 수 있습니다. lastest의 default는 true값으로, 이는 5초가 지난 이후, 가장 마지막으로 발생했던 이벤트 값을 방출하게 됩니다.

Buffer

buffer는 Observable에서 발생하는 이벤트들을 각기 emit하는 것이 아니라, 지정한 시간동안. 혹는 지정한 갯수만큼 들어온 이벤트를 배열로 묶어 emit해주는 역할을 합니다.

 

예제 코드 두가지를 통해 확인해 봅시다.

Observable.from(1...10).buffer(timeSpan: .seconds(1), count: 3, scheduler: MainScheduler.instance)
    .debug()
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)

// Output
2021-04-17 19:32:34.856: ViewController.swift:38 (viewDidLoad()) -> subscribed
2021-04-17 19:32:34.859: ViewController.swift:38 (viewDidLoad()) -> Event next([1, 2, 3])
[1, 2, 3]
2021-04-17 19:32:34.859: ViewController.swift:38 (viewDidLoad()) -> Event next([4, 5, 6])
[4, 5, 6]
2021-04-17 19:32:34.859: ViewController.swift:38 (viewDidLoad()) -> Event next([7, 8, 9])
[7, 8, 9]
2021-04-17 19:32:34.859: ViewController.swift:38 (viewDidLoad()) -> Event next([10])
[10]
2021-04-17 19:32:34.859: ViewController.swift:38 (viewDidLoad()) -> Event completed
2021-04-17 19:32:34.859: ViewController.swift:38 (viewDidLoad()) -> isDisposed

먼저 1초의 시간을 지정한 뒤, count를 통해 3개의 이벤트를 묶어서 emit해 주었습니다.

따라서 1부터 10까지 빠르게 이벤트를 발생하게 했을 때, 3개의 이벤트가 배열로 묶여서 들어오는 것을 확인할 수 있었습니다.

다음 예제입니다.

let timer = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)

timer.buffer(timeSpan: .seconds(4), count: 10, scheduler: MainScheduler.instance)
    .debug()
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)
    
    
// Output
2021-04-17 19:37:56.674: ViewController.swift:36 (viewDidLoad()) -> subscribed

2021-04-17 19:38:00.677: ViewController.swift:36 (viewDidLoad()) -> Event next([0, 1, 2])
[0, 1, 2]
2021-04-17 19:38:04.677: ViewController.swift:36 (viewDidLoad()) -> Event next([3, 4, 5, 6, 7])
[3, 4, 5, 6, 7]
2021-04-17 19:38:08.677: ViewController.swift:36 (viewDidLoad()) -> Event next([8, 9, 10, 11])
[8, 9, 10, 11]
2021-04-17 19:38:12.678: ViewController.swift:36 (viewDidLoad()) -> Event next([12, 13, 14, 15])
[12, 13, 14, 15]
2021-04-17 19:38:16.678: ViewController.swift:36 (viewDidLoad()) -> Event next([16, 17, 18, 19])
[16, 17, 18, 19]

1초에 한번씩 이벤트를 발생시키는 시퀀스가 존재합니다. 이러한 시퀀스에 buffer를 통해 4초 동안 들어오는 이벤트를 배열로 묶어 방출하도록 하였습니다.

 

<참고>

ReactiveX Docs

Rhyno님 블로그 debounce, throttle 포스팅