Skip to content

Instantly share code, notes, and snippets.

@rxhanson
Forked from simme/debounce-throttle.swift
Created April 21, 2020 13:55
Show Gist options
  • Save rxhanson/9d52bc73f7c181a871908dd7ebc71d8d to your computer and use it in GitHub Desktop.
Save rxhanson/9d52bc73f7c181a871908dd7ebc71d8d to your computer and use it in GitHub Desktop.
Swift 3 debounce & throttle
//
// 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