应该限制函数的可能执行次数。因此,在调用函数之后,应该在一段时间内忽略任何重复调用。如果在此期间有呼叫,则应在该时间段之后执行最后一次呼叫。
这是我使用core.async的方法。这里的问题是,在通道c中总结了额外的呼叫。我需要一个内部只有一个位置的通道,它会被put覆盖!每次。
XmlPullParserException Binary XML file line #17<vector> tag requires viewportWidth > 0
用法:
(defn throttle [f time]
(let [c (chan 1)]
(go-loop []
(apply f (<! c))
(<! (timeout time))
(recur))
(fn [& args]
(put! c (if args args [])))))
有没有人知道如何解决这个问题?
(def throttled (throttle #(print %) 4000))
(doseq [x (range 10)]
(throttled x))
; 0
;... after 4 seconds
; 9
答案 0 :(得分:2)
您可以使用debounce功能。
我会在这里复制出来:
(defn debounce [in ms]
(let [out (chan)]
(go-loop [last-val nil]
(let [val (if (nil? last-val) (<! in) last-val)
timer (timeout ms)
[new-val ch] (alts! [in timer])]
(condp = ch
timer (do (>! out val) (recur nil))
in (recur new-val))))
out))
仅当in
未向ms
发送消息时,才会将其发送的最后一个值转发到out
频道。虽然in
在发射之间没有足够长的暂停时继续发射,但是连续丢弃所有但最后消息。
我测试了这个功能。等待4秒然后打印出9
,这几乎是你要求的 - 需要一些调整!
(defn my-sender [to-chan values]
(go-loop [[x & xs] values]
(>! to-chan x)
(when (seq xs) (recur xs))))
(defn my-receiver [from-chan f]
(go-loop []
(let [res (<! from-chan)]
(f res)
(recur))))
(defn setup-and-go []
(let [in (chan)
ch (debounce in 4000)
sender (my-sender in (range 10))
receiver (my-receiver ch #(log %))]))
这是debounce
的版本,将根据问题的要求输出,立即为0,然后等待4秒,然后是9:
(defn debounce [in ms]
(let [out (chan)]
(go-loop [last-val nil
first-time true]
(let [val (if (nil? last-val) (<! in) last-val)
timer (timeout (if first-time 0 ms))
[new-val ch] (alts! [in timer])]
(condp = ch
timer (do (>! out val) (recur nil false))
in (recur new-val false))))
out))
我使用了log
而不是print
。您不能使用println/print
的普通core.async
函数。有关说明,请参阅here。
答案 1 :(得分:2)
要解决您的频道问题,您可以使用带滑动缓冲区的chan:
user> (require '[clojure.core.async :as async])
nil
user> (def c (async/chan (async/sliding-buffer 1)))
#'user/c
user> (async/>!! c 1)
true
user> (async/>!! c 2)
true
user> (async/>!! c 3)
true
user> (async/<!! c)
3
这样只会在下一个时间间隔计算放入通道的最后一个值。
答案 2 :(得分:1)
这取自David Nolens blog's source code:
(defn throttle*
([in msecs]
(throttle* in msecs (chan)))
([in msecs out]
(throttle* in msecs out (chan)))
([in msecs out control]
(go
(loop [state ::init last nil cs [in control]]
(let [[_ _ sync] cs]
(let [[v sc] (alts! cs)]
(condp = sc
in (condp = state
::init (do (>! out v)
(>! out [::throttle v])
(recur ::throttling last
(conj cs (timeout msecs))))
::throttling (do (>! out v)
(recur state v cs)))
sync (if last
(do (>! out [::throttle last])
(recur state nil
(conj (pop cs) (timeout msecs))))
(recur ::init last (pop cs)))
control (recur ::init nil
(if (= (count cs) 3)
(pop cs)
cs)))))))
out))
(defn throttle-msg? [x]
(and (vector? x)
(= (first x) ::throttle)))
(defn throttle
([in msecs] (throttle in msecs (chan)))
([in msecs out]
(->> (throttle* in msecs out)
(filter #(and (vector? %) (= (first %) ::throttle)))
(map second))))
您可能还想在频道中添加dedupe
传感器。
答案 3 :(得分:0)
我需要传递一个函数来捕获args,因为我将其用于输入事件,并且传递了可变对象。 ?
(defn throttle-for-mutable-args [time f arg-capture-fn]
(let [c (async/chan (async/sliding-buffer 1))]
(async-m/go-loop []
(f (async/<! c))
(async/<! (async/timeout time))
(recur))
(fn [& args]
(async/put! c (apply arg-capture-fn (or args []))))))
我用像
[:input
{:onChange (util/throttle-for-mutable-args
500
#(really-use-arg %)
#(-> % .-target .-value))}]