0
votes

I am using jet for asynchronous ring adapter. Jet also comes with async http-client which returns a channel whose value's :body is also a channel.

Also, async server route handler can return a map whose :body key can contain a channel. When this channel would be closed, the response would be returned to the client.

I am writing following go code :

 (defn- api-call-1 []
     (go (-> (jet-client/get "api-url-1")
             <!
             :body                ;; jet http client :body is also a channel.
             <!
             api-call-1-response-parse)))


 (defn- api-call-2 []
     (go (-> (jet-client/get "api-url-2")
             <!
             :body
             <!
             api-call-2-response-parse)))


 (defn route-function []
    (let [response-chan (chan)]
      (go 
        (let [api-call-1-chan (api-call-1) ;; using channel returned by go
              api-call-2-chan (api-call-2)]
              (-> {:api-1 (<! api-call-1-chan)
                   :api-2 (<! api-call-2-chan)}
                  encode-response
                  (>! response-chan)))
        (close! response-chan))
    ;; for not blocking server thread, return channel in body
    {:body response-chan :status 200}))

In my route-function, i can not block.

Though this code works fine, Is using go in api-call-1 is bad ?

I found that to use <! in api-call-1 i need to put it in a go block. Now i use this go block's channel in route-function. Does this look unidomatic ? I am concerned about not exposing api-call-1-response-parse or even :body as channel to the route-function.

What is the right way to structure go block code and functions ? Should i care about extra go blocks in functions api-call-1/2 ?

1

1 Answers

0
votes

What you have looks much like the equivalent code I have in production. This is quite idiomatic, so I think your code is structured correctly.

The fact that core.async parking opperations can't cross function boundaries stems from the fact that it's written as a macro and needs to process the whole chunk of code at once (or at least while it's lexically available). This tends to make all core.async code come out in the pattern you are using.