关于Scala:Clojure的未来失败

Future failure in Clojure

在Scala中,未来可能会失败,并且可以异步找到它:

1
2
3
4
f onComplete {
  case Success(_) => println("Great!")
  case Failure(t) => println("An error has occurred:" + t.getMessage)
}

您如何将其"翻译"为Clojure?我的阅读使我相信,Clojure的未来/Promise模型不如Scala强大,并且您不能仅仅抓住这样的失败。那么该怎么做呢?

Scala的未来永远都不需要问它的价值-当它做好并准备就绪时,它将告诉您发生了什么(包括它是否失败了-这是这个问题的关键)。这就是我"异步"的意思。 Scala的未来可能处于三种可能的状态之一-未完成,失败完成,成功完成。

Scala中一个用例的典型示例是一个返回Future [T]的远程调用,其中T是您真正想要返回的类型。如果远程JVM关闭,则在超时后将发生case Failure(t)

这是一个非常简单的模型。在这个问题上,我要求一个简单的选择。作为附带评论,很高兴听到Clojure打算在某个时候采用Scala期货模型。


我记得Scala的期货是monads,所以在algo.monadsfluokitten中搜索了合适的东西。最后,我找到了Lenardo Borges的imminent库。我认为这就是您想要的。

在此命名空间声明正确的地方

1
2
3
4
(ns imminent-proof.core
  (:require [imminent.core :as i]
            [clojure.core.match :refer [match]])
  (:import  [imminent.result Success Failure]))

这是失败案例

1
2
3
4
(-> (i/future (/ 1 0))
    (i/on-complete #(match [%]
                     [{Success v}] (prn"success:" v)
                     [{Failure e}] (prn"failure:" e))))

成功案例

1
2
3
4
(-> (i/future (Thread/sleep 1000) 42)
    (i/on-complete #(match [%]
                     [{Success v}] (prn"success:" v)
                     [{Failure e}] (prn"failure:" e))))

这是超时情况

1
2
3
4
5
(-> (i/future (Thread/sleep 1000) 42)
    (i/await 500)
    (i/on-complete #(match [%]
                     [{Success v}] (prn"success:" v)
                     [{Failure e}] (prn"failure:" e))))


future宏只是packageJava Future,而deref reader宏只是用于针对future调用.get()的语法糖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
user=> (source future)
(defmacro future
 "Takes a body of expressions and yields a future object that will
  invoke the body in another thread, and will cache the result and
  return it on all subsequent calls to deref/@. If the computation has
  not yet finished, calls to deref/@ will block, unless the variant of
  deref with timeout is used. See also - realized?."

  {:added"1.1"}
  [& body] `(future-call (^{:once true} fn* [] ~@body)))
nil
user=> (source future-call)
(defn future-call
 "Takes a function of no args and yields a future object that will
  invoke the function in another thread, and will cache the result and
  return it on all subsequent calls to deref/@. If the computation has
  not yet finished, calls to deref/@ will block, unless the variant
  of deref with timeout is used. See also - realized?."

  {:added"1.1"
   :static true}
  [f]
  (let [f (binding-conveyor-fn f)
        fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]
    (reify
     clojure.lang.IDeref
     (deref [] (deref-future fut))
     clojure.lang.IBlockingDeref
     (deref
      [ timeout-ms timeout-val]
      (deref-future fut timeout-ms timeout-val))
     clojure.lang.IPending
     (isRealized [] (.isDone fut))
     java.util.concurrent.Future
      (get [] (.get fut))
      (get [_ timeout unit] (.get fut timeout unit))
      (isCancelled [] (.isCancelled fut))
      (isDone [] (.isDone fut))
      (cancel [_ interrupt?] (.cancel fut interrupt?)))))
nil
user=>

因此测试失败与在Java中进行测试没有什么不同:您捕获到ExecutionException,有关Future的信息,请参见Java文档:

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#get()

因此解决方案是从deref捕获异常,如其他答案所示。


我想知道Clojure是否需要特定的构造来处理期货失败的情况。以下行为我提供了相同的功能:

1
2
(defn f-cond[f func-ok func-fail]
   (future (try (func-ok @f) (catch Exception e (func-fail e)))))

然后:

1
2
@(f-cond (future (throw (Exception."hi")))
   identity #(println"Failed:" (.getCause %)))

产生

1
Failed:  #<Exception java.lang.Exception: hi>