Cómo usar clojure.core.async en Clojure – Resuelto

clojure.core.async es una biblioteca de Clojure que implementa un modelo de concurrencia inspirado en el sistema de canales de Go. Facilita la coordinación entre procesos ligeros (también conocidos como goroutines) mediante el uso de canales que actúan como tuberías para transmitir datos entre estos procesos.

Uso básico de clojure.core.async

Veamos un ejemplo básico para ilustrar cómo funcionan los canales y los procesos asíncronos:

(require '[clojure.core.async :refer [go chan >! <! close!]])

;; Creamos un canal
(def c (chan))

;; Proceso productor: envía datos al canal
(go
  (doseq [x (range 5)]
    (>! (async/timeout 500)) ; Simula un retraso de 500 ms
    (>! c x))
  (close! c))

;; Proceso consumidor: recibe datos del canal
(go
  (loop []
    (if-let [v (<! c)]
      (do
        (println "Recibido:" v)
        (recur))
      (println "Canal cerrado, fin del consumo."))))

Explicación del ejemplo

En este ejemplo:

Buffering en canales

Los canales pueden tener un tamaño de búfer para almacenar datos temporalmente. Esto permite desacoplar los productores de los consumidores en términos de velocidad. Veamos un ejemplo donde un canal usa un búfer de tamaño fijo:

(require '[clojure.core.async :refer [chan >! <! go]])

;; Canal con búfer de tamaño 3
(def buffered-chan (chan 3))

(go
  (doseq [x (range 5)]
    (println "Produciendo:" x)
    (>! buffered-chan x)))

(go
  (loop []
    (if-let [v (<! buffered-chan)]
      (do
        (println "Consumiendo:" v)
        (recur)))))

En este ejemplo, el canal buffered-chan puede contener hasta 3 elementos antes de bloquear al productor. Esto asegura que los datos no se pierdan si el consumidor es más lento.

Pipeline de procesamiento

Una característica poderosa de clojure.core.async es la capacidad de construir pipelines para procesar datos en etapas. Aquí tienes un ejemplo de un pipeline con múltiples etapas:

(require '[clojure.core.async :refer [pipeline-blocking chan >! <!]])

;; Crear un canal de entrada y salida
(def input-chan (chan))
(def output-chan (chan))

;; Pipeline con dos etapas de transformación
(pipeline-blocking 4
                   output-chan
                   (map #(str % " procesado"))
                   input-chan)

;; Producir datos
(go
  (doseq [x (range 5)]
    (>! input-chan x))
  (close! input-chan))

;; Consumir datos procesados
(go
  (loop []
    (if-let [v (<! output-chan)]
      (do
        (println "Resultado:" v)
        (recur))
      (println "Pipeline completado."))))

En este caso, los datos producidos en el canal de entrada input-chan pasan por una etapa de transformación (map #(str % " procesado")), y los resultados finales se envían al canal de salida output-chan.

Ventajas avanzadas de clojure.core.async

clojure.core.async es una herramienta indispensable para aplicaciones donde la concurrencia y el manejo eficiente de tareas asíncronas son requisitos clave.