考虑以下核心异步代码。它打印字符串" tick"每2秒钟:
(go-loop []
(<! (timeout 2000))
(prn "tick")
(recur))
我正在寻找通过功能从外部开始和停止间隔的可能性。
我唯一想到的是:
(def running (atom false))
(defn start []
(reset! running true)
(go (loop []
(<! (timeout 2000))
(prn "tick")
(when @running (recur)))))
(defn stop []
(reset! running false))
这是要走的路吗?或者你会做别的事情吗?
答案 0 :(得分:0)
这就是我要做的事情:
(require '[clojure.core.async :as a])
(defn interval [f msecs]
(let [timing (a/chan)
kickoff
#(a/go
(a/<! (a/timeout msecs))
(a/>! timing true))]
(a/go-loop []
(when (a/<! timing)
(a/go (f))
(kickoff)
(recur)))
(kickoff)
#(a/close! timing)))
示例:
(let [i (interval #(prn "tick") 2000)]
(Thread/sleep 7000)
(i))
;; "tick"
;; "tick"
;; "tick"
;;=> nil
这样,你所有的状态都是本地的,通过通道和块来处理,而不是使用全局原子。由于在循环的每次迭代中完成的工作或多或少是恒定的,因此实际的间隔时间应该非常接近您传入的msecs
的数量。
或者,如果您想保证按时间顺序执行对f
的不同调用,您可以执行以下操作:
(require '[clojure.core.async :as a])
(defn interval [f msecs]
(let [action (a/chan (a/dropping-buffer 1))
timing (a/chan)
kickoff
#(a/go
(a/<! (a/timeout msecs))
(a/>! timing true))]
(a/go-loop []
(when (a/<! action)
(f)
(recur)))
(a/go-loop []
(if (a/<! timing)
(do
(a/>! action true)
(kickoff)
(recur))
(a/close! action)))
(kickoff)
#(a/close! timing)))
(此函数的用法与我之前的interval
函数的用法相同。)
此处对f
的所有调用都在同一个循环中。我使用了丢弃缓冲区,因此如果对f
的呼叫时间超过msecs
,则未来的呼叫可能会开始下降以跟上。
答案 1 :(得分:0)
这是另一种方式,你可以去
(defn start []
(let [ctl (chan 0 )]
(go-loop []
(alt!
(timeout 2000) (do
(prn "tick")
(recur))
ctl (prn "Exit ")))
ctl))
(let [w (start) ]
(Thread/sleep 7000)
(>!! w "stop"))
;;=> "tick"
"tick"
"Exit"
您无需在命名空间中定义状态。启动功能将返回chan以便将来停止。