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

前端 未结 5 1462
鱼传尺愫
鱼传尺愫 2021-02-04 14:56

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) \"         


        
相关标签:
5条回答
  • 2021-02-04 15:02

    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

    0 讨论(0)
  • 2021-02-04 15:07

    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))
    
    0 讨论(0)
  • 2021-02-04 15:10

    I found this thread on google which looks interesting:

    https://groups.google.com/forum/?fromgroups=#!topic/clojure-dev/7BKQi9nWwAw

    http://dev.clojure.org/display/design/Promises

    https://github.com/stuartsierra/cljque/blob/master/src/cljque/promises.clj

    0 讨论(0)
  • 2021-02-04 15:13

    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
    
    0 讨论(0)
  • 2021-02-04 15:26

    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))
    
    
    0 讨论(0)
提交回复
热议问题