Clojure - Futures, Delays and Promises

2 minute read

Futures, Delays and Promises

Futures, delays and promises are easy, lightweight tools for concurrent programming. They allow us to separate task definition, task execution and requiring the result.

Futures

Futures are used to define a task and place it on another thread without requiring the result immediately.

1(future (Thread/sleep 4000)
2        (println "prints after 4 seconds"))
3(println "prints immediately")

When we want to get the result of a future task we can use the value that future macro returns. That value is a reference value that we can use to request the result. We request the result by dereferencing the future with eiter deref or @. Values of a future are cached and dereferencing a future will block if the future hasn’t finished running.

1(let [result (future (println "this prints once")
2                     (+ 1 1))]
3  (println "deref: " (deref result))
4  (println "@: " @result))
5; => this prints once
6; => deref: 2
7; => @: 2

We can pass additional arguments to deref to tell it a number of milliseconds to wait along with the value to return if the call times out.

1(deref (future (Thread/sleep 1000) 0) 10 5)
2; => 5

realized? is used to chech if the future is done running.

Delays

Delays are used to define a task without having to execute it or require the result immediately. They don’t evaluate until we request it by using force. Also the result of a delay is cached like the result of future.

 1(def hello-delay
 2  (delay (println "Hello")
 3          "Hello world"))
 4
 5(force hello-delay)
 6; => Hello
 7; => Hello world
 8
 9(force hello-delay)
10; => Hello world

Promises

A promise is a reference to a value that may not yet be realized, or computed. A promise is created using the promise function, and its value can be set using deliver.

 1(def p (promise))
 2
 3(defn compute-x []
 4  (Thread/sleep 1000)
 5  (deliver p 42))
 6
 7(compute-x)
 8
 9; block until p is delivered 
10(println @p)