0

In javascript I could write some code like:

var app_state={"context":"loading"};

$.get("")
  .then(function(data){
     app_state["data"]=data;
  })
 .then(change("context", "edit"))
 .then(render)

In Clojurescript, I'd like something like:

(-> @app-state
  (assoc :context "loading")
  (assoc :data (GET "")) ;;async call
  (assoc :context "edit")
  (render))

Not like:

(defn handler [] ...

(GET "" {:handler handler}
Halvor Holsten Strand
  • 18,479
  • 16
  • 70
  • 84
Dan Bunea
  • 167
  • 2
  • 10

2 Answers2

0

I put the GET function inside function async-get which returns core.async's channel that eventually contains the result of "GET" operation. Then, I wrap those operations in core.async go block

; require core.async in ns declaration
;; (:require-macros [cljs.core.async.macros :refer [go]])
;; (:require [cljs.core.async :refer [chan <! >! put! take!]])

(defn async-get
  [url]
  (let [ch (chan)]
    (GET url {:handler (fn [resp]
                         (put! ch resp))})
    ch))


(go
  (doto app-state
    (swap! assoc :context "loading")
    (swap! assoc :data (<! (async-get "")))
    (swap! assoc :context "edit")
    (render)))
mavbozo
  • 1,151
  • 8
  • 10
  • I like this solution but I also have a problem with it. If I get an error on my get, my app-state will be in an inconsistent state (:context loading). The way I like to do these things in a single function that is passes to the swap which is also captured in a try catch so in case of an exception/error the app-state isn't swapped, like: ` (try (swap! app-state #(-> # (assoc ... (assoc ... (assoc ...))) (catch ....)` Can I put my async call in the mid. of the other assocs so that in case of an error, no swap is performed? – Dan Bunea Dec 17 '15 at 12:25
  • @user2906524 please create a new stackoverflow question because your new question in my answer's comment expands the scope of your original question. – mavbozo Dec 17 '15 at 21:51
0

This is how we solved it in the end:

(defn fetch-weather [query]
 (safe-run 
  (go
     (-> @model
         (assoc :text (str "Data for " query))
         (assoc :weather
           (let [data (<! (GET< (str " http://api" query)))]
             {
              :temp (- (get-in data ["main" "temp"]) 273.15)
              :city query
              :country (get-in data ["sys" "country"])
             }))
         (assoc :context "edit")
         (swapm! model))))))

and

(defn swapm! [x y]
  (swap! y (fn [xx] x)))

and safe-run is a try catch around it. Having the swap at the end makes sure it is only done if all the other operations were ok. So it is all or nothing.

Dan Bunea
  • 167
  • 2
  • 10