This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-03-31
Channels
- # aws (1)
- # beginners (82)
- # boot (7)
- # cider (1)
- # cljs-dev (13)
- # cljsrn (1)
- # clojure (37)
- # clojure-dev (5)
- # clojure-italy (5)
- # clojure-spec (9)
- # clojure-uk (8)
- # clojurescript (110)
- # community-development (6)
- # datomic (1)
- # devcards (1)
- # fulcro (12)
- # lein-figwheel (1)
- # off-topic (34)
- # portkey (24)
- # protorepl (25)
- # re-frame (4)
- # reagent (29)
- # shadow-cljs (8)
- # spacemacs (11)
- # specter (3)
- # unrepl (1)
- # vim (1)
@petr you can put clj code in a cljc, protected by ?#(:clj …) wrapper, but that wouldn’t magically make it so that your browser app could call it. you still need to take care of all the networking stuff.
One question, it's about transit
If it converts the JSON to structures where all keys are strings... what's the advantage over raw object access ?
Maybe because vectors are properly converted?
It caches commonly used keys so if you pass a bunch of maps that share keys you are sending far less data
That is for talking to a tansit remote
I am talking about reading a JSON response
Can somebody explain me why is this putting nil on the channel ?
(defn handler [ch msg]
(fn [err res body]
(go
(if err
(js/console.error (str "Bad error during " msg) err)
(>! ch (t/read r body)))
(close! ch))))
If I add an extra (prn (t/read r body))
inside this function I can see the correctly parsed object, but outside it's still nil. I'm a bit puzzledShould r be res?
nope, sorry for the lack of context
r
is a json reader
(t/reader r :json)
One small question, will a go block wait for all the take instructions ? Even nested ones ?
the go block will create code that appears to wait for for any <! in its scope. note that it can’t see past function boundaries
Here is an example code that I'm not sure if it works properly
(defn finishUpgrade [service]
(go
(if-let [finishUrl (get-in service [:actions :finishupgrade])]
(do (prn (str "About to finish upgrade for " (:name service)))
((<!(Post finishUrl))))
(prn (str "Finish upgrade not required for " (:name service))))
service))
Well, I know it only works when the Post is not executed
Does anyone know how to set up figwheel development for a SPA with no hashes in the client side routing? I’ve looked all over, tried a couple approaches but I can’t seem to serve the index.html page when I have a path with more than one slash in it. I.e. /user
works, /user/
, /user/foo
don’t. I’m getting that behavior using this: https://github.com/mhuebert/figwheel-pushstate-server/blob/master/src/figwheel_server/core.clj and https://github.com/quangv/re-frame-html5-routing/blob/master/src/clj/my_app/dev_server.clj as the :ring-handler
option to figwheel in project.clj
@alex-dixon you mean the issue is that it won’t reload the page when you are in /user/foo
?
@lee.justin.m It’s reproducible with this repo: https://github.com/quangv/re-frame-html5-routing
Client has routes /about and /dashboard, but when I go to /about/ or /about/anything-else I get
Navigated to
app.js:1 Uncaught SyntaxError: Unexpected token <
foo:11 Uncaught ReferenceError: my_app is not defined
at foo:11
@alex-dixon yea but i still don’t know exactly what you mean when you say it doesn’t work. you mean figwheel won’t autoreload or you mean when you manually refresh it doesn’t serve index.html
so it seems like problem one is that your client side routing is messed up because it shouldn’t be making a server call when you navigate there
but that error message is what happens when the server side isn’t serving index.html. i’ve gotten that before too
Understood. But I need the client in charge of everything, so when I navigate to /user/foo I want the server to just defer to the client app (serve “index.html”) and have the client pick up from there
what happen when you hit reload in the browser after getting that message without changing urls?
Same thing. The error above is generated by typing in the URL and pressing enter
Here is an example code that I'm not sure if it works properly
(defn finishUpgrade [service]
(go
(if-let [finishUrl (get-in service [:actions :finishupgrade])]
(do (prn (str "About to finish upgrade for " (:name service)))
((<!(Post finishUrl))))
(prn (str "Finish upgrade not required for " (:name service))))
service))
Ah hah….seems like a problem with no slashes in :asset-path config and the index.html file. Think I got it @lee.justin.m thanks for helping 🙂
the way you wrote it you’re invoking the response from Post
as a function, which also means that the go block won’t wait on the take since its inside a function
I really dislike that fact that that error message is so bad and that logging is off on the server side by default
i had this same issue before and it was such a pain to debug. now that you mention it, it was something about relative paths
Yeah lol…so frustrating. Going to see if I can pr to that repo in hopes of having better CLJS examples for JS devs. Seems to work perfectly now. Thanks for your help really appreciate it
i really want to understand why it is interpreting html as javascript but that’s for another day
@lee.justin.m I think you are right... Previously there was a prn statement there, and I forgot to remove the paren
Let me check...
if you wrap a <! in a prn statement I think it will actually screw with the go block, by the way. you need to let-assign the value and then prn it
Buff, that could become a nested nightmare really fast
Even If I want to discard the result and just wait for the response ?
you have to keep in mind that the go block is a macro that does static analysis on your code. it will look for <! and then transform your code into something else. but it will not cross function boundaries, although it is fine if you have a macro like when
. i probably don’t understand some of the subtleties but that’s how i understand it
yep, could be a bisecting nightmare, there are some sharp edges when using go
blocks in clojurescript, for example having (set! (.-prop obj) "something")
in the go block scope will produce wrong code with hard-to-understand errors, in my case set!
form was expanded from a macro used in a go block, so it took me few hours to figure out what is really going on there, the best approach IMO is to minimize your go block code to absolutely barebone logic and delegate all non-async stuff into separate functions
also some nitpicking-notes about the code posted above: 1) use if-some
instead of if-let
2) clojure/lisp convention is to use kebab-case names for functions and local names
on the off chance that you are a javascript programmer and have ever messed with async/await, it’s the same thing. async does not cross function boundaries, so you have to rewrite those functions to return promises so you can wait on them.
Thanks to both @lee.justin.m for the explanation, I have the same perception as you described. And thanks @darwin for pointing those details, I am a node developer and I tend to go for camel case names
About the if-some vs if-let ... should I make the let and then the if separately ?
as someone who does a lot of js/cljs interop, you’ll be much happier if you use kebab case for cljs and camel case for js. it really helps to keep track of what the hell is going on.
Yes, I am talking from a channel
> it really helps to keep track of what the hell is going on. That makes a lot of sense
I often have camel case in my code, but it always means, “this is a javascript object”
the finish url is not from a channel
the reason is: channel yields nil
when closed, if-let
would go to else branch when nil
or false
in general there may be good cases to put false
on the channel as ordinary value (no closing scenario)
it is subtle, but better to think about this, if you later decided to put false
on some channels, all the places where you if-let
take from those channels would break
also when writing core.async heavy code I would recommend to adopt naming convention where all functions which return channels are prefixed with go-
and functions which would return go channel only in some cases are forbidden - this makes reading and reasoning about such code much more paletable
example: https://github.com/binaryage/dirac/blob/master/src/background/dirac/background/chrome.cljs
I went as far as wrapping core.async with my own macros to have that convention “everywhere”, e.g. timeout
becomes go-wait
later I will add there some poor-man’s detection of some go’s gotchas, e.g. detecting that set!
usage should be pretty easy check in my wrapping go
macro
would it be possible for the go macro to warn right away if it sees a <! behind a function?
it already knows where the boundaries are, so why not just keep moving down the tree?
Thanks for all your tips @darwin
However the code I posted is a bit subtle to what you are describing. Let me explain what it should do and then you explain me how do you think it's better
The function takes an object and returns a channel (that's what the go block allways do right?) I want the function to always return the same thing, the object it gets. If the object contains certain property, then execute a remote call and wait for the response , if not just do nothing about it and also return the same object again to the channel. This will be fairly easy with promises, but I'm not sure if it may be a good practice with channels.
I think it is important that it allways executes asynchronously, I mean for the two possible branches, and I'm not sure if it works this way with channels
yes, IMO it is fine to use ad-hoc channels to pass just one value asynchronously (they work as promises in this scenario) and the other point about all branches return some channel is also reasonable thing to do, functions should either return a channel all the times or never, mixed functions are quite hard to reason about (when you revisit the code later)
@rdanielo sorry, getting later on the boat so I might miss context, but had you tried using the promise-chan
? it has the same behavior as a promise, it will return the same value every time after its resolved
though at some point, one wonders, why mess with channels at all? just use promises
@lee.justin.m if you are in cljs land, you want the go
block to have an async/await
style coding, otherwise you have to keep doing callbacks
for dealing with js promises I have a macro helper that's very handy IMO:
#?(:cljs
(defn promise->chan [p]
(let [c (async/promise-chan)]
(.then p
#(async/put! c {:success %})
#(async/put! c {:error %}))
c)))
#?(:cljs
(defn consumer-pair [resp]
(if (contains? resp :error)
(throw (:error resp))
(:success resp))))
(defmacro <!p [promise]
`(consumer-pair (async/<! (promise->chan ~promise))))
with this, you can use <!p
inside go
blocks to read promises directly, like:
(go
(let [json (-> (js/fetch "some-url") <!p .json <!p)]
(js/console.log "response" json)))
the thing i grapple with is how to ensure i deal with unexpected exception correctly with channels. promises interact with exceptions in a much easier to reason about way
(defmacro go-catch [& body]
`(async/go
(try
~@body
(catch :default e# e#))))
this helps ^^
as you can see, the <!p
will throw an exception when the promise fails
you mean from core async?
but if you think on map
, on a core.async fashion, a lot of times you want to make it parallel
then you can do pipeline-async
, which is more code but has a very efficient result
yeah, prettty much, but instead of <!
I do <?
(defn error? [err]
#?(:clj (instance? Throwable err)
:cljs (instance? js/Error err)))
(defn throw-err [x]
(if (error? x)
(throw x)
x))
(defmacro <? [ch]
`(throw-err (async/<! ~ch)))
because if a channel returns on an exception, this will keep the propagation
I was worried about performance for a while on this too, but I have been doing some aggressive usages of this, and I'm not seeing performance been an issue
creating and disposing channels seems a very lightweight operation
Usually the performance on async operations is not so important since much of the computation/waiting will happen on the remote side and there is nothing you can do about
However error propagation is something that worries me a lot
I'm not sure about this but all the code you posted seems to just throw the errors happily
I don't want my process to crash because an http call have failed
this stuff works well so don’t worry too much about it, but there’s this one bug in cljs-http that really bothers me. if you get bad json from the server, cljs-http will print an uncaught exception to the console and you won’t have a coherent stack trace, because core.async uses nextTick, which doesn’t give you async call stacks. and the go block won’t return, so you can’t deal with it from the consumer side. i have a patch in mind for the library, but the whole thing makes me uncomfortable with core.async
Has anyone seen cases where lein/figwheel doesn’t pull down dependencies? I keep getting “no such namespace cljsjs.chartjs” but I’ve checked that it’s in my project.clj and done several cleans etc. any debugging tips would be great. (FIXED: I deleted everything under target/ and resources/js and that seemed to fix it.
@lee.justin.m you could work-around it in chrome with cljs-devtool’s async feature: https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#what-is-the-async-feature it was recently fixed in Chrome, so with Chrome Canary you don’t even need to use that hack anymore and you get better stack traces