Timeouts in Clojure

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Clojure is straightforward using futures and promises.

(ns timeouts-example
  (:require [clojure.core.async :refer [go chan >! <! timeout alts! <!!)]))

(defn main []
  ; For our example, suppose we're executing an external
  ; call that returns its result on a channel c1
  ; after 2s. Note that we're using a go block to simulate
  ; the asynchronous nature of the operation.
  (let [c1 (chan 1)]
    (go
      (<! (timeout 2000))
      (>! c1 "result 1"))

    ; Here we use alts! to implement a timeout.
    ; It will return the value from whichever channel
    ; is ready first.
    (let [[val port] (alts!! [c1 (timeout 1000)])]
      (if (= port c1)
        (println val)
        (println "timeout 1")))

    ; If we allow a longer timeout of 3s, then the receive
    ; from c2 will succeed and we'll print the result.
    (let [c2 (chan 1)]
      (go
        (<! (timeout 2000))
        (>! c2 "result 2"))

      (let [[val port] (alts!! [c2 (timeout 3000)])]
        (if (= port c2)
          (println val)
          (println "timeout 2"))))))

(main)

Running this program shows the first operation timing out and the second succeeding.

$ clj -M timeouts.clj
timeout 1
result 2

In this Clojure version:

  1. We use clojure.core.async library which provides CSP-style concurrency primitives similar to Go’s channels.

  2. Instead of goroutines, we use go blocks for asynchronous operations.

  3. The select statement in Go is replaced by alts!! function in Clojure, which allows us to wait on multiple channels and returns when any of them becomes ready.

  4. We use timeout function to create a channel that will close after a specified duration, similar to time.After in Go.

  5. The overall structure and logic of the program remains the same, demonstrating how to implement timeouts for asynchronous operations in Clojure.

This example showcases how Clojure can handle concurrent operations and implement timeouts in a manner similar to Go, albeit with its own idiomatic approach.