Skip to content

Instantly share code, notes, and snippets.

@VladimirBrejcha
Last active September 20, 2022 07:06
Show Gist options
  • Save VladimirBrejcha/a3f0c305aa5db3b97919eaa9c108770d to your computer and use it in GitHub Desktop.
Save VladimirBrejcha/a3f0c305aa5db3b97919eaa9c108770d to your computer and use it in GitHub Desktop.
import Foundation
class Throttle {
private let queue: DispatchQueue
private let delay: Double
private var delayedBlock: (() -> Void)?
private var cancelBlock: (() -> Void)?
private var timer: DispatchSourceTimer?
private var isReady = true
private var hasDelayedBlock: Bool { delayedBlock != nil }
init(queue: DispatchQueue, delay: Double) {
self.queue = queue
self.delay = delay
}
func callAsFunction(action: @escaping () -> Void, onCancel: @escaping () -> Void) {
queue.async { [weak self] in
guard let self = self else {
onCancel()
return
}
if self.isReady {
self.isReady = false
action()
self.scheduleTimer()
} else {
self.cancelBlock?()
self.cancelBlock = onCancel
self.delayedBlock = action
}
}
}
private func scheduleTimer() {
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.setEventHandler { [weak self] in
guard let self = self else { return }
if self.hasDelayedBlock {
self.cancelBlock = nil
self.delayedBlock?()
self.delayedBlock = nil
self.scheduleTimer()
} else {
self.isReady = true
}
}
timer.schedule(deadline: .now() + delay)
timer.resume()
self.timer = timer
}
}
// MARK: - Tests -
func print(_ msg: String) {
let formatter = DateFormatter()
formatter.dateFormat = "ss.SSSS"
Swift.print("\(formatter.string(from: Date())) \(msg)")
}
let queue = DispatchQueue(label: "ThrottleQueue")
let throttle: Throttle = Throttle(queue: queue, delay: 0.5)
throttle {
print("action 1 done")
} onCancel: {
print("action 1 cancelled")
}
DispatchQueue.global().async {
throttle {
print("action 2 done")
} onCancel: {
print("action 2 cancelled")
}
}
queue.asyncAfter(deadline: DispatchTime.now() + 0.2) {
throttle {
print("action 3 done")
} onCancel: {
print("action 3 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.3) {
throttle {
print("action 4 done")
} onCancel: {
print("action 4 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.4) {
throttle {
print("action 5 done")
} onCancel: {
print("action 5 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.5) {
throttle {
print("action 6 done")
} onCancel: {
print("action 6 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.7) {
throttle {
print("action 7 done")
} onCancel: {
print("action 7 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.75) {
throttle {
print("action 8 done")
} onCancel: {
print("action 8 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.8) {
throttle {
print("action 9 done")
} onCancel: {
print("action 9 cancelled")
}
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1.3) {
throttle {
print("action 10 done")
} onCancel: {
print("action 10 cancelled")
}
}
queue.asyncAfter(deadline: DispatchTime.now() + 1.5) {
throttle {
print("action 11 done")
} onCancel: {
print("action 11 cancelled")
}
}
queue.asyncAfter(deadline: DispatchTime.now() + 2.5) {
throttle {
print("action 12 done")
} onCancel: {
print("action 12 cancelled")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment