Timers in Clojure

Our first example demonstrates how to use timers in Clojure. We often want to execute code at some point in the future, or repeatedly at some interval. Clojure provides ways to achieve both of these tasks easily.

(ns timers-example
  (:require [clojure.core.async :as async]))

(defn main []
  ; Timers represent a single event in the future. We use
  ; core.async to create a timer channel that will be notified
  ; after a specified time. This timer will wait 2 seconds.
  (let [timer1 (async/timeout 2000)]
    ; The <! blocks on the timer channel until it sends a value
    ; indicating that the timer fired.
    (async/<!! timer1)
    (println "Timer 1 fired")

    ; If you just wanted to wait, you could have used
    ; Thread/sleep. One reason a timer may be useful is
    ; that you can cancel it before it fires.
    ; Here's an example of that.
    (let [timer2 (async/timeout 1000)
          cancel-chan (async/chan)]
      (async/go
        (async/alt!
          timer2 ([] (println "Timer 2 fired"))
          cancel-chan ([] nil)))
      
      ; Cancel the timer
      (async/>!! cancel-chan :cancel)
      (println "Timer 2 stopped")

      ; Give the timer2 enough time to fire, if it ever
      ; was going to, to show it is in fact stopped.
      (Thread/sleep 2000))))

(main)

To run this program, you would typically save it in a file (e.g., timers.clj) and run it using a Clojure runtime environment.

$ clj timers.clj
Timer 1 fired
Timer 2 stopped

The first timer will fire ~2 seconds after we start the program, but the second should be cancelled before it has a chance to fire.

In this Clojure version:

  1. We use clojure.core.async for asynchronous operations, which provides functionality similar to Go’s channels.
  2. The async/timeout function creates a timer channel that closes after the specified duration.
  3. We use async/<!! to block and wait for the timer channel to close.
  4. For the cancellable timer, we use async/alt! to choose between the timer firing and a cancellation signal.
  5. Instead of Go’s goroutines, we use Clojure’s async/go blocks for concurrent operations.

This example demonstrates how to create and use timers in Clojure, including how to cancel them before they fire.