Is there a way to do something like a (Thread/sleep millis my-atom)
that would wake up in case my-atom
got changed before millis?
Or do I have to go clojure.core.async
for that, using channels instead of watches?
You could do this with a promise
, using deref
with a timeout:
(def p (promise))
(future ;; some other thread starts working with the promise
(Thread/sleep 500)
(deliver p :finished-early))
(deref p 1000 :timed-out) ;; => :finished-early
If the sleep took longer than 1000
then deref
would return :timed-out
.
Update: I see your question is now more specifically about atoms. In that case, you could still use a promise by adding a watch on the atom and delivering on the promise if the value changes:
(def p (promise))
(def a (atom {}))
(add-watch a :watch-changed
(fn [_ _ old new]
(when-not (= old new) (deliver p :changed))))
(future
(Thread/sleep 1001)
(swap! a assoc :foo 1))
(deref p 1000 :timed-out) ;; => :changed
Or in reusable function form, r
can be any IRef type:
(defn await-change [r timeout-ms]
(let [p (promise)]
(try
(add-watch r :await-change ;; keyword must be unique per ref!
(fn [_ _ old new]
(when-not (= old new) (deliver p :changed))))
(deref p timeout-ms :timed-out)
(finally
(remove-watch r :await-change)))))
(def a (atom {}))
(future
(Thread/sleep 500)
(swap! a assoc :foo 1))
(await-change a 1000) ;; => :changed