Fork me on GitHub

Could one, using cljc, perform a datomic query from a cljs file?


@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?

Alex Miller (Clojure team)11:03:55

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]
      (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 puzzled


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 ?


@rdanielo what do you mean by nested?


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]
    (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))))


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: and 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?


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


but not when you just try to hit /about


Here is an example code that I'm not sure if it works properly

(defn finishUpgrade [service]
    (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))))


@rdanielo this: ((<!(Post finishUrl))) isn’t right. you need (<! (Post finishUrl))


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.


ah, actually scratch the first note, I thought you are taking from a channel there


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”


you should always write (if-some [thing (<! channel)] <code>)


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


hm that’s a good idea


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?


not sure, that seems like a more difficult analysis


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


right, and that’s why i started using go blocks, but I wonder if it is worth it.


for dealing with js promises I have a macro helper that's very handy IMO:


   (defn promise->chan [p]
     (let [c (async/promise-chan)]
       (.then p
         #(async/put! c {:success %})
         #(async/put! c {:error %}))

   (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:


  (let [json (-> (js/fetch "some-url") <!p .json <!p)]
    (js/console.log "response" json)))


I think totally worth it 🙂

👌 8

hm that’s fun


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]
       [email protected]
       (catch :default e# e#))))


this helps ^^


as you can see, the <!p will throw an exception when the promise fails


it’s nice, but what happens when you start using things like async/map?


you mean from core async?


on #core-async someone told me that i should be using transducers instead of map


this is the kind of complexity that makes me uncomfortable with this stuff


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


maybe the answer is: just use go-catch and <! and everything will be okay


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)

(defmacro <? [ch]
  `(throw-err (async/<! ~ch)))


because if a channel returns on an exception, this will keep the propagation


nothing i do is high performance. i just want small debuggable code


with good ergonomics and readability


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


it’s a network call already so it’s not going to matter for me


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

😥 4

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: 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


oh that’s good news! thanks @darwin