(ns example.asyncq
(:require
[cljs.core.async :as async]
goog.async.AnimationDelay)
(:require-macros
[cljs.core.async.macros :refer [go go-loop]]))
(defn loop-fx-on-atm-when-pred?-is-true [atm fx pred?]
(let [c (async/chan)
f (goog.async.AnimationDelay. #(async/put! c :loop))
a (atom false)]
(add-watch
atm
:loop-fx-on-atm-when-pred?-is-true
(fn [_ _ _ _]
(when-not @a
(async/put! c :initial-loop))))
(go-loop []
(async/<! c)
(reset! a true)
(swap! atm #(if (pred? %) (do (.start f) (fx %)) %))
(reset! a false)
(recur))))
(def the-final-countdown (atom 4))
(loop-fx-on-atm-when-pred?-is-true
the-final-countdown
#(do (js/console.log %) (dec %))
pos?)
(swap! the-final-countdown inc)
;; prints "5" "4" "3" "2" "1", each on a seperate frame (hearbeat)
Назначение loop-fx-on....
состоит в том, чтобы зарегистрировать fx
, который будет вызываться atm
(каждый удар сердца) всякий раз, когда pred? @atm
равно true
.
Я хотел бы следующие свойства:
- Этот код должен выполняться только тогда, когда
pred?
может вернутьtrue
. В противном случае конечный автомат простаивает. - Это не должно регистрироваться более одного раза за кадр. Модификации атома после вызова AnimationDelay, но до следующего фактического кадра фактически являются noops. То есть атом модифицируется, но это не приводит к повторному вызову
f
. - С точки зрения человека, использующего атом, все, что ему нужно сделать, это изменить атом. Им не нужно заботиться о регистрации или отмене регистрации атома для обработки AnimationFrame, он просто работает. Атом будет продолжать обрабатываться в каждом кадре, пока
pred?
не вернет false.
Моя самая большая претензия к приведенному выше коду — это (reset! a true)
и (reset! a false)
вокруг swap!
. Это действительно уродливо, и в неоднопоточной среде, вероятно, будет ошибкой. Есть ли способ улучшить этот код, чтобы я не использовал a
?
Моя вторая проблема заключается в том, что 2 не выполняется. В настоящее время, если вы дважды измените один и тот же атом между кадрами, это фактически приведет к 2 вызовам (f); или 2 обратных вызова на следующем кадре. Я мог бы использовать другой атом, чтобы записать, действительно ли я зарегистрировался для этого фрейма или нет, но это уже становится беспорядочным. Что-нибудь лучше?