This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-04-23
Channels
- # beginners (27)
- # boot (8)
- # cider (17)
- # cljs-dev (8)
- # cljsrn (5)
- # clojure (56)
- # clojure-dev (34)
- # clojure-gamedev (4)
- # clojure-italy (32)
- # clojure-nl (22)
- # clojure-poland (3)
- # clojure-russia (17)
- # clojure-spec (31)
- # clojure-uk (48)
- # clojurescript (47)
- # core-async (41)
- # cursive (13)
- # datomic (22)
- # emacs (9)
- # figwheel (7)
- # fulcro (18)
- # graphql (3)
- # hoplon (15)
- # jobs-discuss (38)
- # keechma (1)
- # luminus (10)
- # off-topic (42)
- # onyx (8)
- # overtone (3)
- # protorepl (5)
- # re-frame (42)
- # reagent (6)
- # reitit (3)
- # schema (4)
- # shadow-cljs (39)
- # slack-help (5)
- # spacemacs (8)
- # specter (1)
- # tools-deps (36)
- # uncomplicate (9)
- # vim (34)
right, you should avoid doing any blocking operations in a go block, use async/thread instead, and integrate it into your go block via the channel it returns
Is there a conceptual difference between
(take!
my-chan
(fn [result]
(do-stuff-with result))
and
(go
(do-stuff-with (<! my-chan)))
?Maybe not conceptual difference. The more clear question is perhaps; should I expect these two pieces of code to behave differently?
We have an example where the first never seems to do-stuff-with
, whereas the second succeeds.
This is in cljs, btw
@noisesmith alright I see, but what did you mean from "via the channel it returns"
async/thread
returns a channel, you can use that channel inside a go block to work with the value that came from a long running calculation
in my experience, otherwise you end up with a bunch of unneeded complexity writing to channels that don't even need to exist
right, core.async/thread exists because otherwise you could easily starve the primary core.async thread pool, not in order to be an effective parallel processing platform
if what you need is to do parallel work extensively you could look into core.reducers or the claypoole library
and what does prevent use to have something that runs a go block on a different thread pool ?
which run the content on a different threadpool rather than the main core async thread pool ?
it would be a change to channels as well - go blocks transform code into callbacks attached to channels
and no, those other libraries are not based on core.async, they are parallelism libraries
@reefersleep no, those two examples compile down to pretty much the same code
ah, there's one difference though. IIRC there's a small chance that the first example will execute when take!
is initially called. Whereas the latter will not start execution until you release control of the main JS thread back to the browser
@tbaldridge I’m uncertain by what you mean with “release control”.
I’m very interested in hearing more about the difference; we use both methods in our code, and it’d be bad if they sometimes produced different results in ways that we are unaware of.
However, it turned out that the exact issue with semantically similar but syntactically different code producing different results resided in other code changes, co-located on that branch and lockstepped with the two above examples.
@reefersleep there are times, when take!
will do as much work as it can before releasing control back to the browser. If there's a value available to take off the callback, then the callback will be invoked directly by take!
otherwise the callback will be inserted into a queue to run later, and take will return right away. This behavior is configurable: https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async.cljs#L100
However, by wrapping code in a go
you're saying " enqueue this go block into the queue to run later, then return with a channel immediately"
so with take!
the callback is sometimes delayed, with a go
it's always delayed
I think I get it now, @tbaldridge. Though, I haven’t dived into the details of the implementation, and I’m a bit confused. Does what you’re saying mean that on-caller?
being truthy ensures synchronous behaviour by take!
?
Otherwise, I don’t see the difference between the callback being “invoked directly by take!
and “inserted into a queue to run later”
Really appreciate your help, btw 🙂
@reefersleep yes, it's a optimization for times where the callback is very short lived (the majority of times). And it shouldn't make a difference, but it could complicate situations where other parts of your code has race conditions.
I can't help much more without seeing the rest of the code
Like I said, the problem we have has been resolved, so this is really more a matter of me trying to learn something that I can pass on to my colleagues. And I have! 😄
On that note, I'd read this code: https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async.cljs#L90-L103 https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async.clj#L15-L22
On the second link you can ignore the body of the function, but notice how the first thing it does is call dispatch/run (enqueuing a callback)
Cheers, that’s what I was reading from it, too 🙂 And thank you for the dispatch explanation!
The code for dispatch/run is here: https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async/impl/dispatch.cljs#L31 The dispatcher is an optimized version of setTimeout. It tries to reduce the overhead of switch back to the browser by running as many callbacks as possible in a given amount of time.