Периодически вызывая функцию в Clojure
Я ищу очень простой способ периодически вызывать функцию в Clojure.
JavaScript setInterval
имеет вид API, который я бы хотел. Если бы я переосмыслил его в Clojure, он выглядел бы примерно так:
(def job (set-interval my-callback 1000))
; some time later...
(clear-interval job)
В моих целях я не возражаю, если это создает новый поток, запускается в пуле потоков или что-то еще. Это не критично, что время тоже точное. Фактически, период, предоставленный (в миллисекундах), может быть просто задержка между окончанием завершения одного вызова и началом следующего.
Ответы
Ответ 1
Также существует довольно много библиотек планирования для Clojure:
(от простого до очень продвинутого)
Прямо из примеров главной страницы github at at at:
(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)
, если вам нужна задержка секунды между окончанием задачи и началом следующего, а не между двумя запусками.
Ответ 2
Если вы хотите очень просто
(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
Прощайте.
Ответ 3
Самый простой подход - просто создать цикл в отдельном потоке.
(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
, чтобы остановить его.
Ответ 4
Я забил код, кодирующий это, со слегка измененным интерфейсом, чем указано в исходном вопросе. Вот что я придумал.
(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")))
Обратная связь приветствуется.
Ответ 5
Другой вариант - использовать java.util.Timer метод schedualeAtFixedRate
edit - мультиплексные задачи на одном таймере и остановка одной задачи, а не всего таймера
(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))
Ответ 6
Вот как я сделал бы версию 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)
Ответ 7
Использование 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)))