Consider the following snippet:
(let [chs (repeatedly 10 chan)]
(doseq [c chs]
(>!! c "hello"))
(doseq [c chs]
(println (<!! c))))
Executing this will hang forever. Why is that?
If I do (go (>! c "hello"))
instead, it works just fine.
That channel has no buffer, and
>!!
is blocking. Refer to the examples for this exact case. They spawn a second thread for this reason - to prevent blocking the main thread. Using a goroutine, as in your question, operates on a similar principle.You can also create the channel with some buffer space -
(chan 1)
To make an asynchronous put, use
clojure.core.async/put!
This works in this example as
<!!
will always unblock because of all necessary puts happening asynchronously. Notice the following things:>!!
and<!!
block the main-thread.go
routines run on the main thread but their code is modified via macroexpansion so that execution control is inverted and they can be parked/executed successively ordered by the laws ofcore.async
channel blocking/buffering logic. This technique is commonly referred to as IOC (inversion of control) state machine.core.async
doesn't even contain>!!
/<!!
. If you write code intended to be ClojureScript compatible, only take from channels withingo
-routines or dispatch values from them in higher order functions passed totake!
and always do puts either ingo
-routines or useput!
.Is
(go (>! ch v))
equivalent to(put! ch v)
?Yes but it's not the same.
put!
is an API wrapper around the channels implementation of thecore.async.impl.protocols/WritePort
put!
method. Macroexpansion of(go (>! ch v))
ends up in the same method call happening but wraps it in lots of generated state-machine code to possibly park the putting operation and pause execution of thego
-routine until a consumer is ready to take fromch
(try to(macroexpand `(go (>! ch v)))
yourself). Spawning a go-block to only do one asynchronous putting operation is kind of a waste and performs worse than callingput!
right away.go
spawns and returns an extra channel that you can take its bodies result from. This offers you to await completion of its execution which you didn't intend to do in your example (aiming at an asynchronous operation).