【问题标题】:Is there a way to be notified when a clojure future finishes?有没有办法在 clojure 未来完成时收到通知?
【发布时间】:2020-12-14 08:38:59
【问题描述】:

有没有办法在 future 上设置 watch 以便在完成时触发回调?

这样的?

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

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

【问题讨论】:

  • (println @a)) 本身在运行 println 之前已经阻塞等待 a 完成。你还想要什么?
  • 另外,你可能真正想要的是NotificationService

标签: clojure


【解决方案1】:

您可以启动另一个监视未来的任务,然后运行该函数。在这种情况下,我将使用另一个未来。这很好地包含在一个何时完成的功能中:

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

【讨论】:

    【解决方案2】:

    对于非常简单的情况: 如果您不想阻塞并且不关心结果,只需在将来的定义中添加回调即可。

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

    如果您关心结果,请在回调中使用 comp

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

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

    从语义上讲,java 等效代码是:

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

    对于复杂的情况,您可能需要查看:

    https://github.com/ztellman/manifold

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

    【讨论】:

      【解决方案3】:
      【解决方案4】:

      已接受答案的扩展

      请注意以下警告:

      使用上述when-done 实现不会调用回调 如果未来被取消。 那是

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

      不会打印,因为取消了 deref 调用 未来会引发异常。

      如果您需要回调发生,我建议:

      (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)))))
      
      

      所以现在会打印出来:

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

      【讨论】:

        【解决方案5】:

        我没有使用 Thread/Sleep 增加额外的时间,而是利用 @future-ref 对未来的任何参考将等到未来完成。

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

        【讨论】:

        • #(@%) 可以更改为deref@ 只是 deref 函数的语法糖。
        最近更新 更多