定期调用Clojure中的函数



我正在寻找一种在Clojure中周期性调用函数的非常简单的方法。

JavaScript的setInterval具有我想要的那种API。如果我在Clojure中重新想象它,它会看起来像这样:

(def job (set-interval my-callback 1000))
; some time later...
(clear-interval job)

出于我的目的,我不介意这是否会创建一个新线程、在线程池中运行或其他什么。时间是否准确也不重要。事实上,所提供的周期(以毫秒为单位)可能只是一个呼叫完成结束和下一个呼叫开始之间的延迟。

如果您想要非常简单的

(defn set-interval [callback ms] 
(future (while true (do (Thread/sleep ms) (callback)))))
(def job (set-interval #(println "hello") 1000))
=>hello
hello
...
(future-cancel job)
=>true

再见。

Clojure还有很多调度库:(从简单到非常高级)

  • 钟声(核心同步集成)
  • 单调
  • 石英岩

直接来自at的github主页示例:

(use 'overtone.at-at)
(def my-pool (mk-pool))
(let [schedule (every 1000 #(println "I am cool!") my-pool)]
(do stuff while schedule runs)
(stop schedule))

如果您希望在任务结束和下一个任务开始之间延迟一秒,而不是在两个开始之间,请使用(every 1000 #(println "I am cool!") my-pool :fixed-delay true)

这就是我使用停止通道执行core.async版本的方法。

(defn set-interval
[f time-in-ms]
(let [stop (chan)]
(go-loop []
(alt!
(timeout time-in-ms) (do (<! (thread (f)))
(recur))
stop :stop))
stop))

以及的使用

(def job (set-interval #(println "Howdy") 2000))
; Howdy
; Howdy
(close! job)

最简单的方法是在一个单独的线程中有一个循环。

(defn periodically
[f interval]
(doto (Thread.
#(try
(while (not (.isInterrupted (Thread/currentThread)))
(Thread/sleep interval)
(f))
(catch InterruptedException _)))
(.start)))

您可以使用Thread.interrupt():取消执行

(def t (periodically #(println "Hello!") 1000))
;; prints "Hello!" every second
(.interrupt t)

您甚至可以使用future来包装循环,使用future-cancel来停止循环

我尝试了一下编码,使用了一个比原始问题中指定的稍微修改过的接口。以下是我的想法。

(defn periodically [fn millis]
"Calls fn every millis. Returns a function that stops the loop."
(let [p (promise)]
(future
(while
(= (deref p millis "timeout") "timeout")
(fn)))
#(deliver p "cancel")))

欢迎反馈。

另一个选项是使用java.util.Timer的scheduleAtFixedRate方法

编辑-在单个计时器上多路复用任务,并停止单个任务而不是整个计时器

(defn ->timer [] (java.util.Timer.))
(defn fixed-rate 
([f per] (fixed-rate f (->timer) 0 per))
([f timer per] (fixed-rate f timer 0 per))
([f timer dlay per] 
(let [tt (proxy [java.util.TimerTask] [] (run [] (f)))]
(.scheduleAtFixedRate timer tt dlay per)
#(.cancel tt))))
;; Example
(let [t    (->timer)
job1 (fixed-rate #(println "A") t 1000)
job2 (fixed-rate #(println "B") t 2000)
job3 (fixed-rate #(println "C") t 3000)]
(Thread/sleep 10000)
(job3) ;; stop printing C
(Thread/sleep 10000)
(job2) ;; stop printing B
(Thread/sleep 10000)
(job1))

使用core.async

(ns your-namespace
(:require [clojure.core.async :as async :refer [<! timeout chan go]])
)
(def milisecs-to-wait 1000)
(defn what-you-want-to-do []
(println "working"))
(def the-condition (atom true))
(defn evaluate-condition []
@the-condition)
(defn stop-periodic-function []
(reset! the-condition false )
)
(go
(while (evaluate-condition)
(<! (timeout milisecs-to-wait))
(what-you-want-to-do)))

最新更新