Handling errors core async, when it's used in async/await style

112 Views Asked by At

I am using core async. In the scenario I care for, I am using it similar to how one would use async / await (all processes are kicked off by a caller, and eventually return)

Problem: Propagating errors

By default, go-blocks fail silently when there are errors

(defn go-uh-oh
  []
  (go
    (throw (Exception. "uh-oh"))))

(<!! (go
      (let [a-chan (go-uh-oh item-one)
            b-chan (go-uh-oh item-two)]
       [(<! a-chan) (<! b-chan)])) 

The top-level take would simply return [nil nil]. Instead, it would be great if we propagated errors and threw.

Potential Solution: go-try

To solve this, it looks like the idiomatic solution is to introduce the go-try and <? macro. The go-try macro wraps the body of a go block in a try-catch, and returns the error if it exists. <? then takes from these kind of go-blocks. If the item is an error, it re-throws. This way errors propagate. Here's an example implementation: https://github.com/alexanderkiel/async-error

So if we wrote the above like so, errors propagate:

(defn go-uh-oh
  [arg]
  (go-try
   (throw (Exception. "uh-oh"))))

(<??
 (go-try (let [a-chan (go-uh-oh 1)
               b-chan (go-uh-oh 2)]
           [(<? a-chan) (<? b-chan)])))

Errors would propagate and we would get the result.

Buut, problem with go-try

But there's a problem with using go-try and <?. Now, if I have some intermediate functions to compose some go-try blocks, errors would get lost again. For example:

(<?? (a/into [] (a/merge [(uh-oh-go-try) (uh-oh-go-try)])))

Here, <?? would see a vector of [Error, Error], and would not propagate anything up.

I could change the way <?? is implemented, and have it consider arrays of errors too. But I wonder if there is a better way. How do you handle error propagation in this case?

0

There are 0 best solutions below