DispatchQueue로 throttle/debounce 실현
소개
UITextField등으로 문자가 입력되었을 때에, 그 캐릭터 라인을 기초로 API를 두드리는 일이 있다고 생각합니다.
그 때에 서버 부하등도 고려해, API를 두드리는 빈도를 좁히거나 한다고 생각합니다.
RxSwift의 throttle/debounce
)에서 RxSwift를 도입 할 수없는 일도 있을까 생각합니다.
그러한 경우, scheduledTimer(timeInterval:target:selector:userInfo:repeats:) 등을 이용해 구현하게 된다고 생각합니다만, timer를 관리하거나 timer가 실행되었을 때에 핸들 하는 메소드를 구현하거나 하지 않으면 안됩니다.
그래서 DispatchQueue
의 extension으로서 throttle/debounce
와 같은 구현을 호출할 수 있도록 해 갑니다.
이용시 샘플
UIViewController 내에서, UITextField 가 전회의 입력시로부터 일정의 초수 이내에 입력이 없었을 때에, text 를 print 하는 구현은 이하와 같이 구현할 수 있게 됩니다.
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
//0.5秒ごとに処理をglobalQueueで行うClosureをPropertyとして保持
let debounceAction = DispatchQueue.global().debounce(delay: .milliseconds(500))
override func viewDidLoad() {
super.viewDidLoad()
//UITextFieldのtextの変更通知の監視登録
let nc = NotificationCenter.default
nc.addObserver(self,
selector: #selector(ViewController.textFieldTextDidChange(_:)),
name: .UITextFieldTextDidChange,
object: nil)
}
@objc private func textFieldTextDidChange(_ notification: Notification) {
debounceAction { [unowned self] in
//前回の入力から0.5秒以内に入力がなかった際にtextをprint
print(self.textField.text)
}
}
}
throttle 구현
먼저 throttle을 구현합니다.
throttle은 イベント発生中であっても、一定時間は同じ処理を実行しない
라는 거동입니다.DispatchTimeInterval
를 인수로, 実行する処理を引数とした関数
를 반환합니다.
함수의 내부에서는, 마지막에 실행된 시간을 보관 유지하는 lastFireTime 를 정의해, 현재의 시각을 대입합니다.
반환 값이 되는 함수에서 lastFireTime은 참조 전달에 self와 delay를 캡처 목록으로 하고 현재 시간 +delay를 할당한 deadline을 정의합니다.DispatchQueue.asyncAfter(deadline:qos:flags:execute:)
를 사용하여 using 클로저에서 현재 시간과 마지막으로 실행된 시간 +delay 시간을 비교합니다.
현재 시각이 진행되고 있는 경우에, lastFireTime 를 갱신해, 인수로 받고 있던 action 를 실행합니다.
extension DispatchQueue {
func throttle(delay: DispatchTimeInterval) -> (_ action: @escaping () -> ()) -> () {
var lastFireTime: DispatchTime = .now()
return { [weak self, delay] action in
let deadline: DispatchTime = .now() + delay
self?.asyncAfter(deadline: deadline) { [delay] in
let now: DispatchTime = .now()
let when: DispatchTime = lastFireTime + delay
if now < when { return }
lastFireTime = .now()
action()
}
}
}
}
위의 구현과 UITextField의 text를 결합하면 다음과 같은 동작이 발생합니다.
debounce 구현
다음으로 debounce를 구현합니다.
debounce는 前回のイベント発生後から一定時間内に同じイベントが発生するごとに処理の実行を一定時間遅延させ、一定時間イベントが発生しなければ処理を実行する
라는 거동입니다.
기본적인 구현은 throttle과 매우 유사합니다.
throttle과의 차이점은 asyncAfter
를 구현하기 전에 lastFireTime에 현재 시간을 다시 할당하는 부분입니다.
extension DispatchQueue {
func debounce(delay: DispatchTimeInterval) -> (_ action: @escaping () -> ()) -> () {
var lastFireTime: DispatchTime = .now()
return { [weak self, delay] action in
let deadline: DispatchTime = .now() + delay
lastFireTime = .now()
self?.asyncAfter(deadline: deadline) { [delay] in
let now: DispatchTime = .now()
let when: DispatchTime = lastFireTime + delay
if now < when { return }
lastFireTime = .now()
action()
}
}
}
}
위의 구현과 UITextField의 text를 결합하면 다음과 같은 동작이 발생합니다.
마지막으로
이와 같이 throttle/debounce
와 같은 구현을 할 수 있으므로, 꼭 이용해 보세요.
Reference
이 문제에 관하여(DispatchQueue로 throttle/debounce 실현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/marty-suzuki/items/496f211e22cad1f8de19
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
UIViewController 내에서, UITextField 가 전회의 입력시로부터 일정의 초수 이내에 입력이 없었을 때에, text 를 print 하는 구현은 이하와 같이 구현할 수 있게 됩니다.
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
//0.5秒ごとに処理をglobalQueueで行うClosureをPropertyとして保持
let debounceAction = DispatchQueue.global().debounce(delay: .milliseconds(500))
override func viewDidLoad() {
super.viewDidLoad()
//UITextFieldのtextの変更通知の監視登録
let nc = NotificationCenter.default
nc.addObserver(self,
selector: #selector(ViewController.textFieldTextDidChange(_:)),
name: .UITextFieldTextDidChange,
object: nil)
}
@objc private func textFieldTextDidChange(_ notification: Notification) {
debounceAction { [unowned self] in
//前回の入力から0.5秒以内に入力がなかった際にtextをprint
print(self.textField.text)
}
}
}
throttle 구현
먼저 throttle을 구현합니다.
throttle은 イベント発生中であっても、一定時間は同じ処理を実行しない
라는 거동입니다.DispatchTimeInterval
를 인수로, 実行する処理を引数とした関数
를 반환합니다.
함수의 내부에서는, 마지막에 실행된 시간을 보관 유지하는 lastFireTime 를 정의해, 현재의 시각을 대입합니다.
반환 값이 되는 함수에서 lastFireTime은 참조 전달에 self와 delay를 캡처 목록으로 하고 현재 시간 +delay를 할당한 deadline을 정의합니다.DispatchQueue.asyncAfter(deadline:qos:flags:execute:)
를 사용하여 using 클로저에서 현재 시간과 마지막으로 실행된 시간 +delay 시간을 비교합니다.
현재 시각이 진행되고 있는 경우에, lastFireTime 를 갱신해, 인수로 받고 있던 action 를 실행합니다.
extension DispatchQueue {
func throttle(delay: DispatchTimeInterval) -> (_ action: @escaping () -> ()) -> () {
var lastFireTime: DispatchTime = .now()
return { [weak self, delay] action in
let deadline: DispatchTime = .now() + delay
self?.asyncAfter(deadline: deadline) { [delay] in
let now: DispatchTime = .now()
let when: DispatchTime = lastFireTime + delay
if now < when { return }
lastFireTime = .now()
action()
}
}
}
}
위의 구현과 UITextField의 text를 결합하면 다음과 같은 동작이 발생합니다.
debounce 구현
다음으로 debounce를 구현합니다.
debounce는 前回のイベント発生後から一定時間内に同じイベントが発生するごとに処理の実行を一定時間遅延させ、一定時間イベントが発生しなければ処理を実行する
라는 거동입니다.
기본적인 구현은 throttle과 매우 유사합니다.
throttle과의 차이점은 asyncAfter
를 구현하기 전에 lastFireTime에 현재 시간을 다시 할당하는 부분입니다.
extension DispatchQueue {
func debounce(delay: DispatchTimeInterval) -> (_ action: @escaping () -> ()) -> () {
var lastFireTime: DispatchTime = .now()
return { [weak self, delay] action in
let deadline: DispatchTime = .now() + delay
lastFireTime = .now()
self?.asyncAfter(deadline: deadline) { [delay] in
let now: DispatchTime = .now()
let when: DispatchTime = lastFireTime + delay
if now < when { return }
lastFireTime = .now()
action()
}
}
}
}
위의 구현과 UITextField의 text를 결합하면 다음과 같은 동작이 발생합니다.
마지막으로
이와 같이 throttle/debounce
와 같은 구현을 할 수 있으므로, 꼭 이용해 보세요.
Reference
이 문제에 관하여(DispatchQueue로 throttle/debounce 실현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/marty-suzuki/items/496f211e22cad1f8de19
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
extension DispatchQueue {
func throttle(delay: DispatchTimeInterval) -> (_ action: @escaping () -> ()) -> () {
var lastFireTime: DispatchTime = .now()
return { [weak self, delay] action in
let deadline: DispatchTime = .now() + delay
self?.asyncAfter(deadline: deadline) { [delay] in
let now: DispatchTime = .now()
let when: DispatchTime = lastFireTime + delay
if now < when { return }
lastFireTime = .now()
action()
}
}
}
}
다음으로 debounce를 구현합니다.
debounce는
前回のイベント発生後から一定時間内に同じイベントが発生するごとに処理の実行を一定時間遅延させ、一定時間イベントが発生しなければ処理を実行する
라는 거동입니다.기본적인 구현은 throttle과 매우 유사합니다.
throttle과의 차이점은
asyncAfter
를 구현하기 전에 lastFireTime에 현재 시간을 다시 할당하는 부분입니다.extension DispatchQueue {
func debounce(delay: DispatchTimeInterval) -> (_ action: @escaping () -> ()) -> () {
var lastFireTime: DispatchTime = .now()
return { [weak self, delay] action in
let deadline: DispatchTime = .now() + delay
lastFireTime = .now()
self?.asyncAfter(deadline: deadline) { [delay] in
let now: DispatchTime = .now()
let when: DispatchTime = lastFireTime + delay
if now < when { return }
lastFireTime = .now()
action()
}
}
}
}
위의 구현과 UITextField의 text를 결합하면 다음과 같은 동작이 발생합니다.
마지막으로
이와 같이 throttle/debounce
와 같은 구현을 할 수 있으므로, 꼭 이용해 보세요.
Reference
이 문제에 관하여(DispatchQueue로 throttle/debounce 실현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/marty-suzuki/items/496f211e22cad1f8de19
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(DispatchQueue로 throttle/debounce 실현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/marty-suzuki/items/496f211e22cad1f8de19텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)