Fork me on GitHub
Drew Verlee02:09:03

I'm reading through Why interceptors?, and I have some really basic questions... Why are sync and async handled differently in the way the article covers? The difference as I understand it, is the order of the responses might differ. I don't see how that difference manifests itself into the conclusions reached.


@drewverlee I wrote the article, but I'm not sure I understand what you're asking. Unlike synchronous computations, async computations do not return a value immediately, so there must be some way to get the value once it's ready. Async Ring specification says that a handler should call the callback functions, but other possible solutions include core.async channels and promises.


If all your handlers/middlewares/interceptors always return normal values – as is the case for synchronous Ring – everything is easy. Or if you always return core.async channels, then that's simple enough to handle as well, because then you just use core.async. But mixing these gets complicated because your middleware have to support both sync and async cases.

Drew Verlee13:09:23

I assume when we say async function were saying a function which makes a request to another computer, then blocks waiting for the response. I would think the value isn't returned immediately in time, it's just returned next because we're blocking. The response might never come back. I'm confused then, why the sync function handler is different then the async one. As the difference would seem to be only that one blocks and another doesn't right? There issue would seem to be one of ordering if events then, I just don't see how that difference plays out in the two patterns at all Like I believe you said, if the ring middeware returned core asyc channels for sync and async values wouldn't that make them uniform and avoid this duplication?


Right, okay. What I meant by async function (and what is meant by async Ring handlers and async functions in JavaScript) is that instead of blocking, the function returns immediately and the actual return value is returned via some other means – like callbacks or core.async channels.


So a sync function would block and async function would not block.


And yeah, if everybody would return core.async channels for everything, that would work!

Drew Verlee17:09:07

Ok, thanks! But then what allows interceptors to be simpler here? If we're saying middeware is more complex in the async version, isn't the interceptor more complex in the sync version?


I think the real killer feature of interceptors is that the computation is no longer represented as an opaque function but as data that can be inspected and modified. This ultimately enables stuff like async processing but I've also (ab-)used for other things. It might not be useful in every context (as Miikka mentioned it could be slower) but I find it can add some useful extensions to the general idea of middleware


While middleware can just directly call each other, with interceptors you need some code (a library) that chains them together. You can then put the complexity there instead of in the interceptors. W.r.t. the data-driven stuff, that's cool but I haven't really seen it used.


I have used it. At the time I was writing many different scrapers each involving multiple steps. Sometimes a step would fail and it was important to know which particular step that was as well as being able to reproduce the entire context. Interceptor’s context datastructure was ideal for that. Was able to build a system that would allow me to step through the execution after it happened, making debugging a breeze. The data driven mechanics also allowed me to inject steps conditionally, of which each would support the same debugging and introspection facilities.


Ok, sounds cool


I'd like to demo some frontend cljs development for my team so they understand why I'm a bit excited. We primarily use IntelliJ IDEA for development and I don't really get how to set up a figwheel demo in IDEA with send to repl functionality


Hmm think I might have made a mistake 🙂


Wow it turns out it actually works if you read the guide and do what it says to do

👍 8

@drewverlee I wrote the article, but I'm not sure I understand what you're asking. Unlike synchronous computations, async computations do not return a value immediately, so there must be some way to get the value once it's ready. Async Ring specification says that a handler should call the callback functions, but other possible solutions include core.async channels and promises.


What are people using for plotting in cljs? There seem to be a number of vega wrappers out there. I'm specifically interested in being able to get mouse clicks from the graph to select points and such. Bonus points for libraries that have re-frame components. Any recommendations?


@bmaddy have you looked at Highcharts?


Hmm, I did years ago and I don't remember it having very good event handling features. I should probably look again though.


Yep, looks like it have interaction features now. Thanks!


you'll need to license it if you plan to use it commercially though, they watermark everything otherwise


Yeah, I'm going to look into the vega options first because I love oss, but that could be a good option if vega doesn't fit my needs.


Hi guys! What do you recommend to use to make http calls from reagent app to external api?


You have sente as an option if you have a clj backend


please tell me it has something to do with Sente from Go 🙂


how do you folks test Clojurescript macros? do you walk down the forms that a macro returns?


if a parameter to a macro has to be a string, do I need to have a spec on it? it is not evaluated so I would not know how to actually 😄


It's usually advised to put as much of the logic in a function, and wrap that function in a macro


(defn your-macro-fn [...]) ;; easily testable (defmacro your-macro ...)

👍 4

what goes inside the macro? does it expand to the function or just calls it?


and is the function a clj function? meaning has to be in the same macro context?


Yeah, so you would have it expand to your function, which would contain the wealth of your logic


(defn foo-fn [f] ...) --> (defmacro foo [& body] `(foo-fn (fn [] [email protected]))


I don't know if this works for every situation, it's just usually seen as best practise to contain as much of the logic in functions


ok cool thank you very much 😄 😄 will try that, I am trying some compile time elision but not having much success


Personally, I’ve found that macros are good for: - reducing boilerplate - creating symbols that use the same pattern in some boilerplate (e.g. (my-macro x) creates x-fn, x-protocol, x-record) - describing a pattern than works with special forms, like try/catch In each case, I identify which of the above that I’m doing, do just that inside the macro, and put the rest into a helper function

👍 4

(there are probably other things to do with macros, but that’s what I remember right now)


There's a short clojure book on macros that I found helpful, it suggests trying to solve the problem without macros at first


You also want to avoid ending up in 'macro hell'


Hello all, I´m using a javascript lib to record audio from mic. The js object can return a Blob of this audio, how do I send it to browser to download it in clojurescript?


What do you mean "send it to browser"? What do you want to do with it in clojurescript?


I just deployed devcards 0.2.6


updated to work with latest react and play well with npm based dependencies


and many other fixes


@john I could do it with

(let [url (.createObjectURL js/URL (.getRecordingFile @msr))
        linkDownload (.createElement js/document "a")]
    (set! (.-href linkDownload) url)
    (set! (.-download linkDownload) "fernando.mp3")
    (.append (.getElementById js/document "app") linkDownload)
    (.click linkDownload)
    (.revokeObjectURL js/URL url)
    (.remove linkDownload)