-
-
Save rxhanson/9d52bc73f7c181a871908dd7ebc71d8d to your computer and use it in GitHub Desktop.
Swift 3 debounce & throttle
This file contains 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
// | |
// debounce-throttle.swift | |
// | |
// Created by Simon Ljungberg on 19/12/16. | |
// License: MIT | |
// | |
import Foundation | |
extension TimeInterval { | |
/** | |
Checks if `since` has passed since `self`. | |
- Parameter since: The duration of time that needs to have passed for this function to return `true`. | |
- Returns: `true` if `since` has passed since now. | |
*/ | |
func hasPassed(since: TimeInterval) -> Bool { | |
return Date().timeIntervalSinceReferenceDate - self > since | |
} | |
} | |
/** | |
Wraps a function in a new function that will only execute the wrapped function if `delay` has passed without this function being called. | |
- Parameter delay: A `DispatchTimeInterval` to wait before executing the wrapped function after last invocation. | |
- Parameter queue: The queue to perform the action on. Defaults to the main queue. | |
- Parameter action: A function to debounce. Can't accept any arguments. | |
- Returns: A new function that will only call `action` if `delay` time passes between invocations. | |
*/ | |
func debounce(delay: DispatchTimeInterval, queue: DispatchQueue = .main, action: @escaping (() -> Void)) -> () -> Void { | |
var currentWorkItem: DispatchWorkItem? | |
return { | |
currentWorkItem?.cancel() | |
currentWorkItem = DispatchWorkItem { action() } | |
queue.asyncAfter(deadline: .now() + delay, execute: currentWorkItem!) | |
} | |
} | |
/** | |
Wraps a function in a new function that will only execute the wrapped function if `delay` has passed without this function being called. | |
Accepsts an `action` with one argument. | |
- Parameter delay: A `DispatchTimeInterval` to wait before executing the wrapped function after last invocation. | |
- Parameter queue: The queue to perform the action on. Defaults to the main queue. | |
- Parameter action: A function to debounce. Can accept one argument. | |
- Returns: A new function that will only call `action` if `delay` time passes between invocations. | |
*/ | |
func debounce1<T>(delay: DispatchTimeInterval, queue: DispatchQueue = .main, action: @escaping ((T) -> Void)) -> (T) -> Void { | |
var currentWorkItem: DispatchWorkItem? | |
return { (p1: T) in | |
currentWorkItem?.cancel() | |
currentWorkItem = DispatchWorkItem { action(p1) } | |
queue.asyncAfter(deadline: .now() + delay, execute: currentWorkItem!) | |
} | |
} | |
/** | |
Wraps a function in a new function that will only execute the wrapped function if `delay` has passed without this function being called. | |
Accepsts an `action` with two arguments. | |
- Parameter delay: A `DispatchTimeInterval` to wait before executing the wrapped function after last invocation. | |
- Parameter queue: The queue to perform the action on. Defaults to the main queue. | |
- Parameter action: A function to debounce. Can accept two arguments. | |
- Returns: A new function that will only call `action` if `delay` time passes between invocations. | |
*/ | |
func debounce2<T, U>(delay: DispatchTimeInterval, queue: DispatchQueue = .main, action: @escaping ((T, U) -> Void)) -> (T, U) -> Void { | |
var currentWorkItem: DispatchWorkItem? | |
return { (p1: T, p2: U) in | |
currentWorkItem?.cancel() | |
currentWorkItem = DispatchWorkItem { action(p1, p2) } | |
queue.asyncAfter(deadline: .now() + delay, execute: currentWorkItem!) | |
} | |
} | |
/** | |
Wraps a function in a new function that will throttle the execution to once in every `delay` seconds. | |
- Parameter delay: A `TimeInterval` specifying the number of seconds that needst to pass between each execution of `action`. | |
- Parameter queue: The queue to perform the action on. Defaults to the main queue. | |
- Parameter action: A function to throttle. | |
- Returns: A new function that will only call `action` once every `delay` seconds, regardless of how often it is called. | |
*/ | |
func throttle(delay: TimeInterval, queue: DispatchQueue = .main, action: @escaping (() -> Void)) -> () -> Void { | |
var currentWorkItem: DispatchWorkItem? | |
var lastFire: TimeInterval = 0 | |
return { | |
guard currentWorkItem == nil else { return } | |
currentWorkItem = DispatchWorkItem { | |
action() | |
lastFire = Date().timeIntervalSinceReferenceDate | |
currentWorkItem = nil | |
} | |
delay.hasPassed(since: lastFire) ? queue.async(execute: currentWorkItem!) : queue.asyncAfter(deadline: .now() + delay, execute: currentWorkItem!) | |
} | |
} | |
/** | |
Wraps a function in a new function that will throttle the execution to once in every `delay` seconds. | |
Accepts an `action` with one argument. | |
- Parameter delay: A `TimeInterval` specifying the number of seconds that needst to pass between each execution of `action`. | |
- Parameter queue: The queue to perform the action on. Defaults to the main queue. | |
- Parameter action: A function to throttle. Can accept one argument. | |
- Returns: A new function that will only call `action` once every `delay` seconds, regardless of how often it is called. | |
*/ | |
func throttle1<T>(delay: TimeInterval, queue: DispatchQueue = .main, action: @escaping ((T) -> Void)) -> (T) -> Void { | |
var currentWorkItem: DispatchWorkItem? | |
var lastFire: TimeInterval = 0 | |
return { (p1: T) in | |
guard currentWorkItem == nil else { return } | |
currentWorkItem = DispatchWorkItem { | |
action(p1) | |
lastFire = Date().timeIntervalSinceReferenceDate | |
currentWorkItem = nil | |
} | |
delay.hasPassed(since: lastFire) ? queue.async(execute: currentWorkItem!) : queue.asyncAfter(deadline: .now() + delay, execute: currentWorkItem!) | |
} | |
} | |
/** | |
Wraps a function in a new function that will throttle the execution to once in every `delay` seconds. | |
Accepts an `action` with two arguments. | |
- Parameter delay: A `TimeInterval` specifying the number of seconds that needst to pass between each execution of `action`. | |
- Parameter queue: The queue to perform the action on. Defaults to the main queue. | |
- Parameter action: A function to throttle. Can accept two arguments. | |
- Returns: A new function that will only call `action` once every `delay` seconds, regardless of how often it is called. | |
*/ | |
func throttle2<T, U>(delay: TimeInterval, queue: DispatchQueue = .main, action: @escaping ((T, U) -> Void)) -> (T, U) -> Void { | |
var currentWorkItem: DispatchWorkItem? | |
var lastFire: TimeInterval = 0 | |
return { (p1: T, p2: U) in | |
guard currentWorkItem == nil else { return } | |
currentWorkItem = DispatchWorkItem { | |
action(p1, p2) | |
lastFire = Date().timeIntervalSinceReferenceDate | |
currentWorkItem = nil | |
} | |
delay.hasPassed(since: lastFire) ? queue.async(execute: currentWorkItem!) : queue.asyncAfter(deadline: .now() + delay, execute: currentWorkItem!) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment