定期调用Clojure中的函数

Dre*_*kes 17 clojure periodic-task

我正在寻找一种在Clojure中定期调用函数的简单方法.

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

(def job (set-interval my-callback 1000))

; some time later...

(clear-interval job)
Run Code Online (Sandbox Code Playgroud)

出于我的目的,我不介意这是创建一个新线程,在线程池或其他东西中运行.时机也是准确的并不重要.事实上,提供的时间段(以毫秒为单位)可能只是一个呼叫结束和下一个呼叫开始之间的延迟.

A. *_*ebb 28

如果你想要非常简单

(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
Run Code Online (Sandbox Code Playgroud)

再见.

  • 这是非常直接的,但要注意的一件事是:既然你永远不会贬低未来,你永远不会看到任何例外.要么确保回调有一个try-catch来记录所有throwable,要么在上面的代码中添加一个try-catch(可调用). (3认同)

Nie*_*lsK 20

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

直接来自at-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))
Run Code Online (Sandbox Code Playgroud)

使用(every 1000 #(println "I am cool!") my-pool :fixed-delay true),如果你想任务结束之间一秒钟的延迟和明年的开始,而不是两次启动之间.

  • 我几天前编辑了这个问题,添加了另一个库:chime(https://github.com/james-henderson/chime).我看不到我的编辑,所以它丢失了还是罐头? (2认同)

xsc*_*xsc 8

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

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

您可以使用Thread.interrupt()以下命令取消执

(def t (periodically #(println "Hello!") 1000))
;; prints "Hello!" every second
(.interrupt t)
Run Code Online (Sandbox Code Playgroud)

你甚至可以future用来包裹循环并future-cancel阻止它.


Dre*_*kes 8

我对此进行了编码,其界面略微修改,而不是原始问题中指定的界面.这就是我想出来的.

(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")))
Run Code Online (Sandbox Code Playgroud)

欢迎反馈.


小智 8

这就是我如何使用停止通道执行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))
Run Code Online (Sandbox Code Playgroud)

和用法

(def job (set-interval #(println "Howdy") 2000))
; Howdy
; Howdy
(close! job)
Run Code Online (Sandbox Code Playgroud)


Shl*_*omi 7

另一种选择是使用java.util.Timer的schedualeAtFixedRate方法

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

(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))
Run Code Online (Sandbox Code Playgroud)