Clojurescript — необработанная ошибка: ‹! используется не в блоке (go)

Я работаю в Clojurescript и пытаюсь использовать core.async, чтобы получить результат от собственной функции Javascript (я am в браузере) и условно интегрировать его в карту.

У меня есть функция для обертывания собственного вызова браузера (вдохновленный выступлением Тимоти Болдриджа о core.async, примерно 34 мин для интересующихся):

(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))

Я могу использовать это так:

   ;; 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))))))))

Последний пример завершается с ошибкой: Uncaught Error: <! used not in (go ...) block, хотя я думал, что использую такую ​​же структуру.

Что я делаю неправильно ?

Примечание . Я требую этого должным образом из директивы :require-macro, как описано здесь.


person nha    schedule 16.08.2015    source источник


Ответы (1)


Макрос go возьмет переданное ему тело и преобразует все вызовы <!, alt! и >! в конечный автомат. Однако он не войдет в функции:

не может использовать цикл for в блоке ядра. асинхронный?

Под переводом остановок на границах функций я подразумеваю следующее: блок go берет свое тело и переводит его в конечный автомат. Каждый вызов <! >! или alts! (и некоторых других) считается переходом конечного автомата, когда выполнение блока может быть приостановлено. В каждой из этих точек машина превращается в обратный вызов и присоединяется к каналу. Когда этот макрос достигает формы fn, он перестает транслироваться. Таким образом, вы можете вызывать <! только внутри блока go, а не внутри функции внутри блока кода.

Это часть магии core.async. Без макроса go код core.async выглядел бы очень похоже на callback-hell на других языках.

person ClojureMostly    schedule 16.08.2015
comment
Похоже, я часто вижу вас здесь :) Еще раз спасибо. Также, возможно, отредактируйте свою цитату, она обрезана после Так что вы можете звонить только в... - person nha; 16.08.2015
comment
Кроме того, я был бы рад, если бы у вас были указания о том, как структурировать такой код. - person nha; 16.08.2015
comment
@nha Я только что подумал об этом. Это, вероятно, неподходящее место для обсуждения, но может быть полезно подумать, что единицей, которую следует передавать при использовании core.async, является канал. Если вам нужно создать повторно используемые функции, выполняющие асинхронные действия, верните канал. Таким образом, код, использующий эти функции, может делать все, что захочет, настраивая циклы перехода, блоки перехода и блокируя операции ввода/вывода (операторы !!, доступные в clj не в cljs). Таким образом, вы можете избежать проблемы с ОП, вернув канал и выполнив установку вне функции geolocation. - person Conan; 10.12.2015