zcaudate
zcaudate

Reputation: 14258

Is there a way to be notified when a clojure future finishes?

Is there a way to set a watch on a future so that it triggers a callback when it is done?

something like this?

> (def a (future (Thread/sleep 1000) "Hello World!")
> (when-done a (println @a))

...waits for 1sec...
;; =>  "Hello World"

Upvotes: 11

Views: 3402

Answers (5)

Bandoos
Bandoos

Reputation: 93

An extension to the accepted answer

please note the following caveat:

with the above when-done implementation the callback will not be invoked if the future is cancelled. That is

(do
  (def f0 (future (Thread/sleep 1000)))
  (when-done f0 (fn [e] (println "THEN=>" e)))
  (Thread/sleep 500)
  (future-cancel f0))

will not print because the deref call on the cancelled future will raise an exception.

If you need callbacks to happen i suggest:

(defn then
  "Run a future waiting on another, and invoke
  the callback with the exit value if the future completes,
  or invoke the callback with no args if the future is cancelled"
  [fut cb]
  (future
    (try
      (cb @fut)
      (catch java.util.concurrent.CancellationException e
        (cb)))))

So now this will print:

  (do
    (def f0 (future (Thread/sleep 1000)))
    (then f0 (fn
               ([] (println "CANCELLED"))
               ([e] (println "THEN=>" e))))
    (Thread/sleep 500)
    (future-cancel f0))

Upvotes: 2

Kannan Ramamoorthy
Kannan Ramamoorthy

Reputation: 4190

Instead of adding additional time with Thread/Sleep , I'm leveraging on the fact that @future-ref for any reference to the future will wait until the future is done.

(defn  wait-for
  [futures-to-complete]
  (every? #(@%) futures-to-complete))

Upvotes: 0

paulocuneo
paulocuneo

Reputation: 109

For very simple cases: If you dont want to block and dont care about the result just add the callback in the future definition.

(future (a-taking-time-computation) (the-callback))

If you care about the result use comp with the call back

(future (the-callback (a-taking-time-computation)))

or

(future (-> input a-taking-time-computation callback))

Semantically speaking the java equivalent code would be:

final MyCallBack callbackObj = new MyCallBack();
new Thread() {
     public void run() {
         a-taking-time-computation();
         callbackObj.call();
     }
 }.start()

For complex cases you may want to look:

https://github.com/ztellman/manifold

https://github.com/clojure/core.async

Upvotes: 5

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91587

You can start another task that watches the future and then runs the function. In this case I'll just use another future. Which wraps up nicely into a when-done function:

user=> (defn when-done [future-to-watch function-to-call] 
          (future (function-to-call @future-to-watch)))
user=> (def meaning-of-the-universe 
         (let [f (future (Thread/sleep 10000) 42)] 
            (when-done f #(println "future available and the answer is:" %)) 
            f))
#'user/meaning-of-the-universe

... waiting ...

user=> future available and the answer is: 42
user=> @meaning-of-the-universe
42

Upvotes: 17

Related Questions