What is the advantage of core.async in clojurescript? - clojurescript

Everybody is talking about how great core.async is, and how it will improve event handling in clojurescript. I've been following the ClojureScript 101 tutorial, and I don't see any clear advantage from these code examples. What am I missing?
Why is the use of core.async any better here?
(defn listen [el type]
(let [out (chan)]
(events/listen el type
(fn [e] (put! out e)))
out))
(defn dosomethingasync
[e]
(js/console.log "async: " e))
(let [clicks (listen (dom/getElement "search1") "click")]
(go (while true (dosomethingasync (<! clicks)))))
vs.
(defn dosomethingcallback
[e]
(js/console.log "callback: " e))
(events/listen (dom/getElement "search2") "click" dosomethingcallback)

Great question!
I think your first step to understand the advantage would be Timothy Baldridge Video
And below my try:
I think that the differences will clear up if we change a bit your code.
Firstly, trying to highlight the sentence "There comes a time in all good programs when components or subsystems must stop communicating directly with one another" extracted from the presentation of core.async posted on the clojure blog. I think that we can separate the input events channel from the let fn:
(def clicks (listen (dom/getElement "search1") "click"))
(go
(while true
(dosomethingasync (<! clicks))))
(put! clicks "this channel can be written from differents parts of your code")
Secondly, with core.async we can write asynchronous calls as we'll write synchronous calls (sequential code). An example of this situation require more than one channel:
(def clicks (listen (dom/getElement "search1") "click"))
(def keys-pressed (listen (dom/getElement "search1") "keypress"))
(def to-out (chan))
(go
(while true
(let [click-recieved (<! clicks)]
(dosomethingasync click-recieved)
(>! to-out "click recieved!")
(let [other-input-waited (<! keys-pressed)]
(dosomethingwithkey other-input-waited)
(>! to-out "keypressed recieved!")
)
)
))
And lastly, i think that you are not properly using the meaning of callback function. When we talk of a callback function i think we are referring to a function that besides its own parameters it receives a function "callback". At the end of the execution of the function we call the callback function to return the execution flow to the original point.
Changing your "callback" function come up as follow:
(defn dosomethingcallback
[e call-back-fn]
(js/console.log "callback: " e)
(call-back-fn))
And if we try to issue something similar to the same behaviour achieved with previous code core.async example:
(defn do-key
[call-back-fn e]
(.log js/console "before callback key" )
(call-back-fn e))
(defn do-click
[call-back-fn e]
(.log js/console "before callback click")
(call-back-fn e))
(defn key-callback-fn [e]
(.log js/console (str "doing some work with this key: " e))
)
(defn click-callback-fn [e]
(.log js/console (str "doing some work with this click" e))
(events/listen (dom/getElement "search2") "keypress" (partial do-key key-callback-fn)))
(events/listen (dom/getElement "search2") "click" (partial do-click click-callback-fn))

Related

How to return a promise from a go block?

The question is how to send to a nodejs app the result of a go block
i found a solution with callback
but i need a solution with promise
Promise solution?
Clojurescript app
(defn foo []
(go 1))
;;how to change foo,wrap to promise?, so node app can await to get the 1
;;i used 1 for simplicity in my code i have something like
;;(go (let [x (<! ...)] x))
Node app
async function nodefoo() {
var x = await foo();
console.log(x); // i want to see 1
}
Callback solution (the one that works now)
So far i only found a way to pass a cb function, so this 1 goes back to node.js app
Clojurescript app
(defn foo1 [cb]
(take! (go 1)
(fn [r] (cb r))))
Node app
var cb=function () {....};
foo1(cb); //this way node defined cb function will be called with argument 1
But i dont want to pass a callback function, i want node.js to wait and get the value.
I want to return a promise.
This function takes a channel and returns a Javascript Promise that resolves with the first value the channel emits:
(defn wrap-as-promise
[chanl]
(new js/Promise (fn [resolve _]
(go (resolve (<! chanl))))))
Then to show usage:
(def chanl (chan 1))
(def p (wrap-as-promise chanl))
(go
(>! chanl "hello")
(.then p (fn [val] (println val))))
If you compile that and run it in your browser (assuming you called enable-console-print!) you'll see "hello".
It is also possible to extend the ManyToManyChannel type with extend-type.
Here's a naif implementation using a similar wrap-as-promise function
(require '[clojure.core.async.impl.channels :refer [ManyToManyChannel]])
(defn is-error? [val] (instance? js/Error val))
(defn wrap-as-promise
[channel callback]
(new js/Promise
(fn [resolve reject]
(go
(let [v (<! channel)]
(if (is-error? v)
(reject v)
(resolve (callback v))))))))
(extend-type ManyToManyChannel
Object
(then
[this f]
(wrap-as-promise this f)))
(def test-chan (chan 1))
(put! test-chan (new js/Error "ihihi"))
(put! test-chan :A)
(defn put-and-close! [port val]
(put! port val)
(async/close! port))
(-> test-chan
(.then (fn [value] (js/console.log "value:" value)))
(.catch (fn [e] (js/console.log "error" e)))
(.finally #(js/console.log "finally clause.")))

Which reagent Form 2 component function arguments are in use?

Which form is better?
(defn my-func [arg arg2]
(fn [] [:div (str arg arg2)]))
Or this one?
(defn my-func []
(fn [arg arg2] [:div (str arg arg2)]))
I'm writting from my phone so forgive me indentation...
There's other difference between these functions aside from arity.
The first function will close over arg, and arg2 on first render, and use these arguments for each subsequent rerender. E.g.
(def a 1)
(def b 2)
[my-func a b]
will always result in [:div '12'] whether or not a or b were changed
The second function will take arg, and arg2 on each render and will update the result when arguments change.
Look for Rookie mistake in reagent docs
I believe your question should be which one is correct(The first one is correct). The second one has got an arity exception. Based on your use case, I would prefer to define and call the anonymous function so as to get the results. Consider this number one edit:
(defn my-func [arg arg2]
((fn [] [:div (str arg arg2)])))

How do I extract the body from an HTTP request in Clojure?

I am making an HTTP request:
(defn main-panel []
(def API-URL "https://api.chucknorris.io/jokes/random")
(defn getFileTree []
(go (let [response (<! (http/get API-URL
{:with-credentials? false
:headers {"Content-Type" "application/json"}}))]
(:status response)
(js/console.log (:body response))))) ; prints a very complex data structure
(go
(let [result (<! (getFileTree))]
(.log js/console (:body result)))) ; prints null
:reagent-render
(fn []
[:h1 "kjdfkjndfkjn"]))
But I can't get to the "joke" in the returned object, array item 13:
How do I assign this value to a let or def?
Also, why does the second console.log print null?
Update
I am now moving on from using reagent atoms to reframe.
This is my component that successfully GETs data, updates the re-frame 'database':
(defn main-panel []
(def API-URL "https://api.chucknorris.io/jokes/random")
(def request-opts {:with-credentials? false})
(defn getFileTree []
(go (let [response (<! (http/get API-URL request-opts))]
(re-frame/dispatch [:update-quote response]))))
(defn render-quote []
(println (re-frame/subscribe [::subs/quote])) ;successfully prints API data as in screenshot below
(fn []
(let [quote-data (re-frame/subscribe [::subs/quote])
quote-text (if quote-data (:value quote-data) "...loading...")]
[:div
[:h3 "Chuck quote of the day"]
[:em quote-text]])))
(fn []
(getFileTree)
[render-quote]))
But this is the object I get back from the re-frame database:
As you can see it comes wrapped in the Reaction tags and I can't access the body or value any more. How do I access those?
I have a small working version using the reagent template. Create a new project (assuming you have Leiningen installed) with: lein new reagent chuck. This will create a project with many dependencies, but it works out of the box.
Next, edit the file at src/cljs/chuck/core.cljs and edit it so it looks like the following:
(ns chuck.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [reagent.core :as reagent :refer [atom]]
[cljs-http.client :as http]
[cljs.core.async :refer [<!]]))
(def api-url "https://api.chucknorris.io/jokes/random")
(def request-opts {:with-credentials? false
:headers {"Content-Type" "application/json"}})
(def api-response (atom nil))
(defn get-quote []
(go
(let [response (<! (http/get api-url request-opts))]
(println response)
(reset! api-response response))))
(defn render-quote []
(fn []
(let [quote-data (:body #api-response)
quote-text (if quote-data (:value quote-data) "...loading...")]
[:div
[:h3 "Chuck quote of the day"]
[:em quote-text]])))
(defn quote-page []
(fn []
(do
(get-quote)
[:div
[:header
[render-quote]]
[:footer
[:p "footer here"]]])))
;; -------------------------
;; Initialize app
(defn mount-root []
(reagent/render [quote-page] (.getElementById js/document "app")))
(defn init! []
(mount-root))
I'll explain the relevant bits:
init will bootstrap the basics of the front-end, but in our case it's just calls mount-root which starts reagent telling it to call quote-page and placing the results in the DOM replacing the element with the ID of app.
quote-page calls get-quote which will call the API using the cljs-http library. I'm not checking for errors here, but basically when the request completes (either success or error) it will read the results from the channel (using <!) and place the response in response. The key is that response is a nested ClojureScript map that you can inspect to check if the result was successful or not. Note that I'm also printing the results with println instead of JS interop (.log js/console xxx) because console.log will show the inner details of how the nested map is implemented, which is not relevant for this case.
One the response is available, I store the results of the response in an atom called api-response. The key here is that the atom will contain nothing for a bit (while the request completes) and then the response will be inside it and reagent will take care of detecting the change and re-rendering.
Finally, quote-page calls render-quote which generates the markup for rendering the quote or a placeholder while it loads.
To run the whole thing, open a terminal and run lein run which will start a web server listening on port 3000 by default. In another terminal, run lein figwheel which will compile the ClojureScript code for you. One figwheel is ready it will start a REPL, and you can open the address http://0.0.0.0:3000/ in your computer to view the page.

touch left / touch right events in reagent

I'm using reagent to develop a site. It's unclear to me how to work with touch events in reagent, actually, in general. I read this post
clojurescript: touch events and Domina, but do i need to use domina to handle on-touch-start events with reagent ? Would anyone have a snippet of code to detect -- given a dom element -- if a user has swiped a % left or right ?
Reagent uses React's virtual DOM and synthetic events. Touch events are available in React but you have to call (.initializeTouchEvents js/React true) before rendering any component (i.e. before calling reagent.core/render-component).
Edit: the code below won't work because touch events are more complex than mouse events. See this MDN page for some (JS) examples. React seems to implement the same touch API.
For the swipe percentage, do something like the following (untested):
(defn swipe-element []
(let [swipe-state (atom {:width 0, :start-x 0, :current-x 0})]
(fn []
[:div {:on-touch-start
(fn [e]
(reset! swipe-state {:width (.. e -target -offsetWidth)
:start-x (.. e -target -pageX)
:current-x (.. e -target -pageX)}))
:on-touch-move
(fn [e]
(swap! swipe-state assoc :current-x (.. e -target -pageX)))}
(let [{:keys [width start-x current-x]} #swipe-state
direction (if (> current-x start-x) "right" "left")]
(str "Swipe " direction ": "
(* (/ (.abs js/Math (- start-x current-x)) width) 100) "%")))))

Is there any way to make an onClick handler in Om without using anonymous function?

I want to make a click handler function for an Om component. The docs and Stack Overflow examples I've found always declare anonymous functions like this
(defn main-view [_ owner]
(reify
om/IRender
(render [_]
(let [xs (items)]
(dom/div nil
(om/build sub-view {:title "View A"})
(om/build sub-view {:title "View B"})
(dom/button
#js {:onClick
(fn [e] (om/transact! xs #(assoc % 1 {:text "zebra"})))}
"Switch To Zebra!"))))))
I think it's cleaner to declare click functions outside the jsx/template area, within the component, the way it's commonly done in regular React. Is there a way do this in Om within the component? I tried this, but it doesn't work because onClick is undefined:
(defn my-component []
(reify
om/IRender
(render [this]
; Using Sablono syntax
(html [:h1 "Here is a heading" {:on-click 'onClick} ]))
onClick
(onClick [this]
; this part never gets executed when you click
(.log js/console "click"))))
I'd like to avoid defining a separate function outside the component if it's possible.
Your question is sensible and it's about handling scope of data.
It is possible but the problem with this approach in most cases you will need local scope data from the outside code block (in your case, it's an Om component).
I would explain in code. Let's say you want to move handler function out:
(anything
(let [a 1 b 2]
(on-event (fn my-handler [evt] (log (+ a b (.someAttr evt)))))))
You'll end up with this which is way longer:
(defn local-data->handler [a b]
(fn [evt] (log (+ a b (.someAttr evt)))))
(anything
(let [a 1 b 2]
(on-event (local-data->handler a b))))
in case you just want to move around inside the component definition:
(anything
(let [a 1
b 2
my-handler (fn [evt] (log (+ a b (.someAttr evt))))]
(on-event my-handler)))
Please note: to keep event handler work, ensure your non-anonymous function (created with defn or let) is the same as the anonymous form, especially argument list.
onClick is undefined because you use it as if it's an Om protocol. Please consult Om lifecycle protocols for correct usage.
https://github.com/swannodette/om/wiki/Documentation
Per your requirements, you should move the function definition out of the component.
You should then be able to pass the function's name to the event listener:
(defn foo [] (println "foo"))
(defn my-component [text owner]
(reify
om/IRender
(render [_]
(dom/button
#js { :onClick foo }
"Click Here"))))