Skip to content

Instantly share code, notes, and snippets.

@oliyh
Last active August 29, 2025 03:29
Show Gist options
  • Select an option

  • Save oliyh/0c1da9beab43766ae2a6abc9507e732a to your computer and use it in GitHub Desktop.

Select an option

Save oliyh/0c1da9beab43766ae2a6abc9507e732a to your computer and use it in GitHub Desktop.
Debounce in Clojure on the JVM
(import '[java.util Timer TimerTask])
(defn debounce
([f] (debounce f 1000))
([f timeout]
(let [timer (Timer.)
task (atom nil)]
(with-meta
(fn [& args]
(when-let [t ^TimerTask @task]
(.cancel t))
(let [new-task (proxy [TimerTask] []
(run []
(apply f args)
(reset! task nil)
(.purge timer)))]
(reset! task new-task)
(.schedule timer new-task timeout)))
{:task-atom task}))))
;; example usage
(def say-hello (debounce #(println "Hello" %1)))
(say-hello "is it me you're looking for?")
(say-hello "Lionel")
;; one second later...
;; Hello Lionel
@jyn514
Copy link

jyn514 commented Aug 29, 2025

this fixed it for me:

   (let [timer (Timer.)
         task (atom nil)]
     (fn [& args]
       (let [new-task (proxy [TimerTask] []
                        (run []
                          (apply f args)
                          (reset! task nil)
                          (.purge timer)))
             old ^TimerTask @task]
         ; NOTE: we never retry this;
         ; an outdated value means we already scheduled a rerun,
         ; and we don't promise that all events get through.
         (when (compare-and-set! task old new-task)
           (when old (.cancel old))
           (.schedule timer new-task ms))))))

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