gpt4 book ai didi

clojure - 使用线程宏进行惯用的错误/异常处理

转载 作者:行者123 更新时间:2023-12-02 11:05:58 24 4
gpt4 key购买 nike

我使用 http 请求从 API 一次获取数千个实体。作为管道的下一步,我想将它们全部放入数据库中。

(->> ids
(pmap fetch-entity)
(pmap store-entity)
(doall))

fetch-entity 需要一个 String id 并尝试使用 http 请求检索实体,然后返回 Map 或引发异常(例如由于超时)。

store-entity 需要一个 Map 并尝试将其存储在数据库中。它可能会引发异常(例如,如果 Map 与数据库架构不匹配或者根本没有收到 Map)。

不优雅的错误处理

我的第一个“解决方案”是编写包装函数 fetch-entity'store-entity' 来捕获它们各自原始函数的异常。

fetch-entity' 在失败时返回其输入,基本上在 http 请求失败时传递 String id。这确保了整个管道继续运输。

store-entity' 检查其参数的类型。如果参数是一个Map(获取实体成功并返回一个Map),它会尝试将其存储在数据库中。

如果尝试存储到数据库时引发异常,或者如果 store-entity' 传递了 String (id) 而不是 Map 它将conjerror_ids的外部Vector

这样我以后就可以使用 error_ids 来确定发生故障的频率以及哪些 id 受到影响。

感觉上面的方法不是实现我想要做的事情的明智方法。例如,我编写 store-entity' 的方式使用前一个管道步骤 (fetch-entity') 来完成该函数,因为它的行为根据前一个管道步骤是否为成功与否。

同时让 store-entity' 了解名为 error_ids 的外部 Vector 感觉根本不对。

有没有一种惯用的方法来处理这种情况,在这种情况下,您有多个管道步骤,其中一些步骤可能会抛出异常(例如,因为它们是 I/O),而我无法轻松地使用谓词来确保函数将表现可预测,我不想打扰管道,只是稍后检查在哪些情况下出错了?

最佳答案

可以使用 Try monad 类型,例如 cats library :

It represents a computation that may either result in an exception or return a successfully computed value. Is very similar to the Either monad, but is semantically different.

It consists of two types: Success and Failure. The Success type is a simple wrapper, like Right of the Either monad. But the Failure type is slightly different from Left, because it always wraps an instance of Throwable (or any value in cljs since you can throw arbitrary values in the JavaScript host).

(...)

It is an analogue of the try-catch block: it replaces try-catch’s stack-based error handling with heap-based error handling. Instead of having an exception thrown and having to deal with it immediately in the same thread, it disconnects the error handling and recovery.

基于堆的错误处理正是您想要的。

下面我做了一个fetch-entitystore-entity的例子。我让 fetch-entity 在第一个 id (1) 上抛出 ExceptionInfo ,并且 store-entity 抛出 DivideByZeroException在第二个 ID (0) 上。

(ns your-project.core
(:require [cats.core :as cats]
[cats.monad.exception :as exc]))


(def ids [1 0 2]) ;; `fetch-entity` throws on 1, `store-entity` on 0, 2 works


(defn fetch-entity
"Throws an exception when the id is 1..."
[id]
(if (= id 1)
(throw (ex-info "id is 1, help!" {:id id}))
id))


(defn store-entity
"Unfortunately this function still needs to be aware that it receives a Try.
It throws a `DivideByZeroException` when the id is 0"
[id-try]
(if (exc/success? id-try) ; was the previous step a success?
(exc/try-on (/ 1 (exc/extract id-try))) ; if so: extract, apply fn, and rewrap
id-try)) ; else return original for later processing


(def results
(->> ids
(pmap #(exc/try-on (fetch-entity %)))
(pmap store-entity)))

现在,您可以分别使用 success?failure? 过滤成功或失败的结果,并通过 cats- 检索值摘录

(def successful-results
(->> results
(filter exc/success?)
(mapv cats/extract)))

successful-results ;; => [1/2]


(def error-messages
(->> results
(filter exc/failure?)
(mapv cats/extract) ; gets exceptions without raising them
(mapv #(.getMessage %))))

error-messages ;; => ["id is 1, help!" "Divide by zero"]

请注意,如果您只想循环遍历错误成功结果,则可以使用传感器,如下所示:

(transduce (comp
(filter exc/success?)
(map cats/extract))
conj
results))
;; => [1/2]

关于clojure - 使用线程宏进行惯用的错误/异常处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46238593/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com