I want to calculate the rate of events, for example, key press events, and display it. I also want to start rate calculation only after first event happened. Another requirement is that rate calculation should stop after certain amount of key press events. My current approach to that is
(defn keypress-chan
"Returns a channel that receives keys"
[]
(let [out (chan) handler (fn [e] (put! out (.-key e)))]
(js/document.addEventListener "keypress" handler false)
out))
(defn tick
"Pushes to `out` 1000 ms later"
[out]
(go
(<! (timeout 1000))
(>! out 1)))
(defn get-rate
"Calculates keypress per minute rate"
[ticks pressed]
(quot (* pressed 60) ticks))
(defn complete?
[count]
(>= count 100))
(defn main-loop []
(let [key-chan (keypress-chan) ticker-chan (chan)]
(go-loop [pressed 0 ticks 0]
(let [[value ch] (alts! [key-chan ticker-chan])]
(if (= ch key-chan)
(do
;; Start timer to calculate keypress rate
(if (zero? pressed)
(tick ticker-chan))
;; Do something with `value`, i. e. the key being pressed
(when-not (complete?)
(recur (inc pressed) ticks)))
(let [speed (get-rate ticks pressed)]
(swap! app-state assoc :rate rate)
(tick ticker-chan)
(recur pressed (inc ticks))))))))
So, this works, but the code in main-loop seems ugly to me. I've been thinking of having a "trigger" that fires only when first value appears in the key-chan and starts the timer. Then another trigger stops the timer when necessary. But I'm new to Clojure and don't know well how to work with async, so I couldn't create anything better than code above.
How this loop could be improved?
Can I move timer to a function and start the timer by the first value in the key-chan?
How can I stop that timer later?