Of course I want to wrap various requests to external services with core.async, while still returning results from these operations through some chan
.
I want to take care of both thrown exceptions and timeouts (ie that the operation takes longer than expected to return, or to be able to choose among various services for the same task but with different approaches or qualities of service.
The smallest viable example to show examples of both being able to handle an error, a timeout and a correct returning result seems to be these:
(require '[clojure.core.async :refer [chan go timeout <! >! alt!]])
(def logchan (chan 1))
(go (loop []
(when-let [v (<! logchan)]
(println v)
(recur))))
(dotimes [_ 10]
(go
(let [result-chan (chan 1)
error-chan (chan 1)
timeout-chan (timeout 100)]
(go
(try
(do (<! (timeout (rand-int 200)))
(>! result-chan (/ 1 (rand-int 2))))
(catch Exception e (>! error-chan :error))))
(>! logchan (alt! [result-chan error-chan timeout-chan]
([v] (if v v :timeout)))))))
This code prints something like
1
:error
1
:error
:error
:timeout
:error
:timeout
:timeout
This is not very elegant. I especially don't like the way of returning :error
and :timeout
. The nil
-check in alt!
is clearly not what I want either.
Is there some better way to accomplish the three goals of returning result, protect from long timeouts and handle errors? The syntax is quite OK (most things above are really to provoke those three errors).
The core.async chan
-function has the ex-handler, so the following construct is possible
(chan buf-or-n xform ex-handler)
where ex-handler
is a one-argument function that gets the exception. When the function returns nil
, this is not put on the channel, otherwise the function has the chance to transform the exception to something viable for the data in question.
Put output in one channel, errors and results.
Define records (maps) for errors such as
(defrecord SomeError [cause context etc...])
This way the errors will hold relevant info.
Verify output in consumers
(if-not (instance? SomeError result) ...)
There is no one 'correct' way, you can use pub/sub for the same purpose, pass in one error-chan or create several and async/merge them together.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With