Debounce and Throttle - leonoel/missionary GitHub Wiki
Debounce and throttle are popular techniques to deal with events happening in bursts. Events in a burst are sometimes related and redundant, therefore it is acceptable to discard some of them in order to save resources.
This discrete input flow produces successive integers in 5 bursts of 5.
(def input
(->> (m/ap (m/? (m/sleep (m/?> (m/seed (cycle [100 10 10 10 10]))))))
(m/eduction (take 25) (map-indexed (fn [i _] i)))))
Debounce delays the propagation of each event by a fixed duration, in order to detect a burst. When the burst is over, the last event of the burst is emitted.
Throttle also ensures a minimal duration between events, but emits the first event of the burst immediately and discards after.
input ----------XXXXX----------XXXXX----------XXXXX----------XXXXX----------XXXXX-------
debounce --------------------X--------------X--------------X--------------X--------------X-
throttle ----------X-----X--------X-----X--------X-----X--------X-----X--------X-----X-----
Debounce
Debouncing is a special case of switching, the continuation is a sleep
where the cancellation error is ignored.
(import '(missionary Cancelled))
(defn debounce [dur >in]
(m/ap
(let [x (m/?< >in)]
(try (m/? (m/sleep dur x))
(catch Cancelled e
(m/amb))))))
(tests
(m/? (->> input
(debounce 50)
(m/reduce conj)))
:= [4 9 14 19 24])
Throttle
Throttle is essentially about backpressure management, it can be implemented with a relieve
discarding oldest values followed by an ap
to enforce a minimal delay between successive events.
(defn throttle [dur >in]
(m/ap
(let [x (m/?> (m/relieve {} >in))]
(m/amb x (do (m/? (m/sleep dur)) (m/amb))))))
(tests
(m/? (->> input
(throttle 50)
(m/reduce conj)))
:= [0 4 5 9 10 14 15 19 20 24])