clojure periodic-task

Periódicamente llamando a una función en Clojure



periodic-task (7)

Así es como haría la versión core.async con el canal de parada.

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

Y el uso

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

Estoy buscando una forma muy sencilla de llamar a una función periódicamente en Clojure.

setInterval de JavaScript tiene el tipo de API que me gustaría. Si lo volviera a imaginar en Clojure, se vería algo así:

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

Para mis propósitos, no me importa si esto crea un nuevo hilo, se ejecuta en un grupo de hilos o algo más. No es crítico que el tiempo sea exacto tampoco. De hecho, el período provisto (en milisegundos) solo puede ser un retraso entre el final de una llamada y el comienzo de la siguiente.


El enfoque más simple sería tener solo un bucle en un hilo separado.

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

Puedes cancelar la ejecución usando Thread.interrupt() :

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

Incluso podría usar el future para envolver el bucle y future-cancel en el future-cancel para detenerlo.


Intenté codificar esto, con una interfaz ligeramente modificada que la especificada en la pregunta original. Esto es lo que se me ocurrió.

(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")))

Comentarios bienvenidos.


Otra opción sería usar el método schedualeAtFixedRate de java.util.Timer

editar: multiplexar tareas en un solo temporizador y detener una sola tarea en lugar de todo el temporizador

(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))


Si quieres muy sencillo

(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

Adiós.


También hay bastantes bibliotecas de programación para Clojure: (de lo simple a lo avanzado)

Directamente de los ejemplos de la página de inicio de github de 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))

Use (every 1000 #(println "I am cool!") my-pool :fixed-delay true) si desea una demora de un segundo entre el final de la tarea y el comienzo de la siguiente, en lugar de entre dos inicios.


Usando 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)))