3

When I evaluate the following core.async clojurescript code I get an error: "Uncaught Error: <! used not in (go ...) block"

(let [chans [(chan)]]
  (go
   (doall (for [c chans]
     (let [x (<! c)]
       x)))))

What am I doing wrong here? It definitely looks like the <! is in the go block.

glts
  • 21,808
  • 12
  • 73
  • 94
user229487
  • 1,387
  • 10
  • 14
  • 5
    This is a constraint of `go` blocks : `<!` won't work inside a `for` for instance. See [the accepted answer](http://stackoverflow.com/questions/26040928/couldnt-use-for-loop-in-go-block-of-core-async) for a similar question. – T.Gounelle Feb 26 '15 at 08:30
  • @user229487 please mark the right post as answer – Itamar Jan 08 '18 at 13:21

2 Answers2

5

because go blocks can't cross function boundaries I tend to fall back on loop/recur for a lot of these cases. the (go (loop pattern is so common that it has a short-hand form in core.async that is useful in cases like this:

user> (require '[clojure.core.async :as async])
user> (async/<!! (let [chans [(async/chan) (async/chan) (async/chan)]]
                   (doseq [c chans]
                     (async/go (async/>! c 42)))
                   (async/go-loop [[f & r] chans result []]
                     (if f
                       (recur r (conj result (async/<! f)))
                       result))))
[42 42 42]
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
1

Why dont you use alts! from Core.Async?

This function lets you listen on multiple channels and know which channel you read from on each data.

For example:

(let [chans [(chan)]]
  (go
    (let [[data ch] (alts! chans)]
        data)))))

You can ask of the channel origin too:

...
(let [slow-chan (chan)
      fast-chan (chan)
      [data ch] (alts! [slow-chan fast-chan])]
    (when (= ch slow-chan)
       ...))

From the Docs:

Completes at most one of several channel operations. Must be called inside a (go ...) block. ports is a vector of channel endpoints, which can be either a channel to take from or a vector of [channel-to-put-to val-to-put], in any combination. Takes will be made as if by !. Unless the :priority option is true, if more than one port operation is ready a non-deterministic choice will be made. If no operation is ready and a :default value is supplied, [default-val :default] will be returned, otherwise alts! will park until the first operation to become ready completes. Returns [val port] of the completed operation, where val is the value taken for takes, and a boolean (true unless already closed, as per put!) for put

Doumentation ref

Itamar
  • 1,601
  • 1
  • 10
  • 23