Clojurescript - Uncaught Error: <! used not in

2019-08-09 07:24发布

问题:

I am in Clojurescript, and trying to use core.async to get a result from a native Javascript function (I am in the browser), and conditionally integrate it into a map.

I have a function to wrap a native browser call (inspired by Timothy Baldridge's talk on core.async, approx. 34 min for those interested) :

(defn geolocation []
    (let [c (chan)
         cb (fn [e] (put! c (js->clj e)))]
     (if (exists? js/navigator.geolocation)
       (.getCurrentPosition js/navigator.geolocation. cb cb)
       (put! c {:error "Geolocation is not supported"}))
     c))

I can use it like that :

   ;; this works
   (go (.log js/console "RES1 -"  (<! (geolocation))))

   ;; this too (not sure how to print it though)
   (go (println "RES2 - "  (-> {}
                            (assoc :test (<! (geolocation))))))

   ;; this fails... why ?
   (go (println "RES3 - "  (-> {}
                               (#(when true
                                   (assoc % :test (<! (geolocation))))))))

The last example fails with an error : Uncaught Error: <! used not in (go ...) block, even though I thought I was using the same kind of structure.

What am I doing wrong ?

Note : I am requiring it properly from the :require-macro directive, like described here.

回答1:

The go macro will take the body it's given and transform all <! and alt! and >! calls to a state machine. However it won't walk into functions:

couldn't use for loop in go block of core.async?

By stops translation at function boundaries, I mean this: the go block takes its body and translates it into a state-machine. Each call to <! >! or alts! (and a few others) are considered state machine transitions where the execution of the block can pause. At each of those points the machine is turned into a callback and attached to the channel. When this macro reaches a fn form it stops translating. So you can only make calls to <! from inside a go block, not inside a function inside a code block.

This is part of the magic of core.async. Without the go macro, core.async code would look a lot like callback-hell in other langauges.