1

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.

nha
  • 17,623
  • 13
  • 87
  • 133

1 Answers1

3

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.

Community
  • 1
  • 1
ClojureMostly
  • 4,652
  • 2
  • 22
  • 24
  • Looks like I am seeing you a lot around here :) Thanks again. Also maybe edit your citation, it is cut after "So you can only make calls to"... – nha Aug 16 '15 at 19:50
  • Also, I'd be happy if you have pointers about how to structure this kind of code. – nha Aug 16 '15 at 20:11
  • 1
    @nha I've just been thinking about this. This probably isn't the right place to discuss it, but it might help to think that the unit to pass around when using core.async is the channel - if you need to make reusable functions that do async stuff, return the channel. That way code that uses those functions can do whatever it wants, setting up go-loops, go blocks and blocking puts/takes (the `!!` operators, available in clj [not in cljs](http://stackoverflow.com/a/21401449/513684)). So you can avoid the OP's problem by returning the channel and doing the put outside the `geolocation` function – Conan Dec 10 '15 at 14:38