启动并停止core.async Interval

时间:2016-12-22 13:08:54

标签: clojure core.async

考虑以下核心异步代码。它打印字符串" 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))

这是要走的路吗?或者你会做别的事情吗?

2 个答案:

答案 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以便将来停止。