Skip to content

Instantly share code, notes, and snippets.

@fjcaetano
Last active September 23, 2021 07:25
Show Gist options
  • Save fjcaetano/ff3e994c4edb4991ab8280f34994beb4 to your computer and use it in GitHub Desktop.
Save fjcaetano/ff3e994c4edb4991ab8280f34994beb4 to your computer and use it in GitHub Desktop.
Debounce + Throttle
import Dispatch
private var throttleWorkItems = [AnyHashable: DispatchWorkItem]()
private var lastDebounceCallTimes = [AnyHashable: DispatchTime]()
private let nilContext: AnyHashable = arc4random()
public extension DispatchQueue {
/**
- parameters:
- deadline: The timespan to delay a closure execution
- context: The context in which the throttle should be executed
- action: The closure to be executed
Delays a closure execution and ensures no other executions are made during deadline
*/
public func throttle(deadline: DispatchTime, context: AnyHashable? = nil, action: @escaping () -> Void) {
let worker = DispatchWorkItem {
defer { throttleWorkItems.removeValue(forKey: context ?? nilContext) }
action()
}
asyncAfter(deadline: deadline, execute: worker)
throttleWorkItems[context ?? nilContext]?.cancel()
throttleWorkItems[context ?? nilContext] = worker
}
/**
- parameters:
- interval: The interval in which new calls will be ignored
- context: The context in which the debounce should be executed
- action: The closure to be executed
Executes a closure and ensures no other executions will be made during the interval.
*/
public func debounce(interval: Double, context: AnyHashable? = nil, action: @escaping () -> Void) {
if let last = lastDebounceCallTimes[context ?? nilContext], last + interval > .now() {
return
}
lastDebounceCallTimes[context ?? nilContext] = .now()
async(execute: action)
// Cleanup & release context
throttle(deadline: .now() + interval) {
lastDebounceCallTimes.removeValue(forKey: context ?? nilContext)
}
}
}
@edvinassabaliauskas
Copy link

edvinassabaliauskas commented Sep 21, 2021

Hey 👋, quite a neat solution!
But I see few issues:

  1. Default context (nilContext) is same for different dispatch queues which gives quite unexpected behaviour. Default context could simply be a queue label.
  2. Global vars are not thread-safe. Issue could easily arise when throttling/debouncing actions from DispatchQueue.global().
  3. You also mixed throttle with debounce and vice versa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment