debounce
공식문서는 debounce가
이벤트 간 특정한 시간간격이 지난 후 elements를 publish 한다고 설명합니다.
공식문서에서는 다음과 같이 debounce 에 대해 설명하고 있습니다!
- debounce operator를 사용하면 업스트림 publisher 로부터 value의 전송 간격과 value 의 개수를 제어할 수 있다.
- 이 operator 는 다운스트림에 전달되는 값의 수를 지정한 속도로 줄여야하는 bursty 혹은 대량 이벤트 스트림을 처리하는데 유용하다.
저는 이번에 검색기능을 구현하면서 요 기능을 찾아보게 되었어요!
textfield 에 값을 입력할때마다 api call 을 하면 불필요하고 너무 많은 api 를 요청하게 되기 때문에 일정 시간을 두고 검색을 하려고 해요! 성능은 중요하니깐..+ 서버비용,,,,!
debounce 특징
- 입력주기가 끝나면 마지막 값을 출력한다
- 추가로 value 가 들어오면 주기가 다시 갱신된다
공식문서에 예제로 함께 보면,
/* DEBOUNCE */
let bounces:[(Int,TimeInterval)] = [
(0, 0),
(1, 0.25), // 0.25s interval since last index
(2, 1), // 0.75s interval since last index
(3, 1.25), // 0.25s interval since last index
(4, 1.5), // 0.25s interval since last index
(5, 2) // 0.5s interval since last index
]
let subject = PassthroughSubject<Int, Never>()
let cancellable = subject
.debounce(for: .seconds(0.5), scheduler: RunLoop.main)
.sink { index in
print ("Received index \\(index)")
}
for bounce in bounces {
DispatchQueue.main.asyncAfter(deadline: .now() + bounce.1) {
subject.send(bounce.0)
}
}
마블다이어그램으로 그림과 같이 나타낼 수 있습니다.
보라색 시점에 값이 방출되고,
추가로 value 가 들어오면 이 주기는 다시 갱신이 되고
노란색은 값이 출력되기 전 실행되는 주기입니다! (0.5)
노란색이 끝나는 시점에서 마지막 값이 downstream 으로 가게 됩니다
throttle
throttle 도 먼저 공식문서를 보도록 할게요
throttle 은 특정시간 간격안에서 업스트립 publisher가 publish한 가장 최근의 값 or 첫번째 element 를 publish 합니다
그래서 그런지 인자중에 latest: Bool 이 있더라구요
공식문서에서는 추가로 설명하는 부분이 있는데
- throttle 을 사용해서 사용자가 특정한 간격 동안 upstream publisher 로 부터 선택적으로 다시 publish 할수있다는 것이다..? (뭔소리죠,,?)
- throttling 간격동안 upstream 에서 받은 다른 elements 는 다시 publish 되지 않는다
라고 설명하는데 이해가 잘 안가서 찾아보다가 felix-ios 블로그의 글을 보고 이해했습니다.
https://felix-mr.tistory.com/10
throttle 은 가장 최근의 값 혹은 첫번째 값 만을 publish 하기 때문에
- 첫번째 값일 경우 : 시간이 경과되기 전까지 첫번째 이후에 업스트림으로 부터 publish 된 값들을 무시
- 가장 최근 값의 경우 : 시간이 경과되기 전 시점에서 자미작 요소 이전에 업스트림으로부터 publish 된 값들은 무시
라는 당연한 소리였어요,,,
너무 말이 어려웠습니다 🥹
throttle 특징
- 처음 구독 시점에 첫번째 value를 바로 한번 방출합니다
- 첫번째값인 경우 (latest: false) : 특정 주기 안의 첫번째 value 를 출력
- 가장 최근값인 경우 (latest: true) : 특정 주기 안의 가장 최근value 출력
요건 버튼 여러번 터치할때 처음 터치만 받고 일정 간격지나고 보내게 한다면 좋을 것 같아요!
아래 예제는 latest 가 true인 경우에요!
/* THROTTLE */
let cancellable = Timer.publish(every: 3.0, on: .main, in: .default)
.autoconnect()
.print("➡️\\(Date().description)")
.throttle(for: 10.0, scheduler: RunLoop.main, latest: true)
.sink(
receiveCompletion: { print ("🔥Completion: \\($0).") },
receiveValue: { print("✅ Received Timestamp \\($0).") }
)
디버깅 창을 보면 요렇게 나와요
그림으로 한번 그려봤습니다
false 로 바꿨을때는, 즉 첫번째를 내보내는 경우는
아래와 같은 결과가 나옵니다!
보면 첫번째 값이 received timestamp 로 찍힌걸 볼 수 있죠??
요기까지 debounce 와 throttle 에 대해 알아봤습니다!
References
Combine) debounce와 throttle의 차이
[Combine 책 정리] Chapter 6: Time Manipulation Operators