【问题标题】:Future failure in ClojureClojure 未来的失败
【发布时间】:2015-10-28 05:37:43
【问题描述】:

在 Scala 中,future 可能会失败,这可以异步发现:

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

您如何将其“翻译”成 Clojure?我的阅读使我相信 Clojure 的未来/承诺模型不如 Scala 强大,而且你不能像这样抓住失败。那么该怎么做呢?

永远不需要询问 Scala 未来的价值——当它良好并准备好时,它会告诉你发生了什么(包括它是否失败——这就是这个问题的症结所在)。这就是我所说的“异步”。 Scala 未来可以处于三种可能的状态之一 - 未完成、失败完成、成功完成。

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

这是一个非常简单的模型。在这个问题中,我要求一个简单的替代方案。作为附带评论,很高兴听到 Clojure 打算在某个时候采用 Scala Futures 模型。

【问题讨论】:

  • 我的理解是期货在 Clojure 中不是很常用。我看到很多人在谈论core.async,但我可能错了。
  • futures、core.async、agents 和常规 java 线程池都有自己的位置,它们都不能很好地替代其他线程池
  • @mfirry 我明白为什么他们会被避免。对未来进行“获得价值”是没有意义的。我只是想找出经验丰富的 Clojure 开发人员实际使用的简单替代方案。例如,Om 处理失败时客户端状态的回滚,因此 Om 如何将失败引入我们的代码将给我一个提示。我才刚刚开始研究 Clojure,所以我自己也不知道答案。

标签: scala clojure


【解决方案1】:

我记得 Scala 中的 future 是 monad,所以搜索了 algo.monadsfluokitten 以寻找合适的东西。最后我找到了 Lenardo Borges 的 imminent 库。我想这就是你想要的。

有了这个命名空间声明

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

这是失败案例

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

这是成功案例

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

这是超时情况

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

【讨论】:

  • 很好的答案。有一天也能在 Clojurescript 上看到它会很高兴 - Scala.js 已经使用了 Scala 期货。
  • 大家好,我是《迫在眉睫》的作者。下一个版本将包括 Clojurescript 支持。
【解决方案2】:

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

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

然后:

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

结果

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

【讨论】:

  • ...并且您必须确保在适当的超时后完成取消引用(获取值/失败),您必须自己管理。 Scala 中非常简单的东西在 Clojure 中变得非常复杂。必须有一个“典型的解决方法”,它不(??)涉及 Clojure/Java 期货。
  • 你是什么意思'合适的超时,你自己管理'?我在上面的代码中没有看到任何超时管理。一旦你运行了未来,它就会开始在另一个线程中执行,并立即返回。如果执行(或失败)的结果在某个时间后可用,则取消引用将花费该时间,
  • 并且在上面的代码中不需要解引用。您可以运行 (f-cond ...) 而不是 @(f-cond ...)。你不会得到执行结果,但是 onSuccess 会在它可用时被执行。
  • 回答你的问题(你自己管理的合适的超时时间)我正在考虑在未来很有可能完成时取消引用,以免阻塞太多,但得到结果一个及时的时尚。这就是您必须自己进行的管理。
  • 在很有可能完成时检索计算结果 - 该语言的具体情况如何?
【解决方案3】:

future 宏只是包装了一个 Java Future,而 deref 读取器宏只是用于针对 future 调用 .get() 的语法糖:


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,请参阅 java doc for Future:

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

所以解决办法是从deref中捕获异常,如其他答案所示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-19
    • 2015-03-12
    • 2018-04-21
    • 2011-08-23
    • 2019-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多