Last active
June 24, 2020 11:13
-
-
Save KaQuMiQ/b2fbabb2fa208b9d83e1a8d99bf70b74 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import struct Dispatch.DispatchTime | |
| import protocol Dispatch.DispatchSourceTimer | |
| import class Dispatch.DispatchSource | |
| import class Dispatch.DispatchQueue | |
| import struct Foundation.NSDate.TimeInterval | |
| import class Foundation.NSLock.NSRecursiveLock | |
| public final class Throttle { | |
| public let queue: DispatchQueue | |
| private var task: (() -> Void)? | |
| private var timer: DispatchSourceTimer? { | |
| didSet { oldValue?.cancel() } | |
| } | |
| private let intervalMicroseconds: UInt64 | |
| private let rescheduleOnPush: Bool | |
| private let lock: NSRecursiveLock = NSRecursiveLock() | |
| public init(interval: TimeInterval, rescheduleOnPush: Bool = false, queue: DispatchQueue) { | |
| self.queue = queue | |
| self.rescheduleOnPush = rescheduleOnPush | |
| self.intervalMicroseconds = UInt64(interval * 1000000000) | |
| } | |
| deinit { | |
| lock.lock() | |
| timer?.cancel() | |
| } | |
| public func push(_ task: @escaping () -> Void, on queue: DispatchQueue? = nil) { | |
| lock.lock() | |
| defer { lock.unlock() } | |
| self.task = task | |
| guard self.timer == nil || rescheduleOnPush else { return } | |
| self.timer = scheduleTimer() | |
| } | |
| public func cancel() { | |
| lock.lock() | |
| defer { lock.unlock() } | |
| self.timer = nil | |
| self.task = nil | |
| } | |
| private func execute() { | |
| lock.lock() | |
| defer { lock.unlock() } | |
| assert(timer != nil) | |
| defer { | |
| timer?.cancel() | |
| timer = nil | |
| } | |
| guard let task = self.task else { return assertionFailure("Missing task on throttle execution.") } | |
| defer { self.task = nil } | |
| task() | |
| } | |
| private func scheduleTimer() -> DispatchSourceTimer { | |
| let timer = DispatchSource.makeTimerSource(queue: queue) | |
| timer.schedule(deadline: DispatchTime(uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + intervalMicroseconds)) | |
| timer.setEventHandler { [weak self] in | |
| guard let self = self else { return } | |
| self.execute() | |
| } | |
| timer.activate() | |
| return timer | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example:
Task will be executed on specified DispatchQueue while being delayed by interval. On subsequent
pushmethod calls the task will be replaced until specified time interval passes counting from first push when Throttle was idle. Pushing new tasks after execution will cause next timer start.