Clojure future and promise — Simple Async with @deref and deliver | Episode 28
0views
C
CelesteAI
Description
Two small primitives that cover the most common async patterns. `future` takes a body and runs it on a background thread — you get a reference back, and `@` deref returns the body's value when it's ready. `promise` is an empty cell that any thread can `deliver` a value into — `@` blocks until someone does. Both are one-shot: once set, the value is fixed.
In this episode we run three `(future (slow-double n))` calls in parallel and collect their results in input order, then demo a classic producer/consumer handoff where the main thread blocks on `@p` until a background future delivers.
Student code: https://github.com/GoCelesteAI/clojure-for-beginners/tree/main/episode28
Every keystroke is shown on screen with generous pauses so you can follow along at your own pace.
What You'll Learn:
- `(future body)` — run `body` on a background thread; returns a future reference
- `@future` / `(deref future)` — block until done, return the value (cached after)
- `(realized? f)` — non-blocking check for done
- `(promise)` — create an empty cell
- `(deliver p v)` — fill a promise once; subsequent delivers are no-ops
- `@promise` — block until delivered
- Parallel fan-out: run N futures, `(map deref fs)` preserves input order
- When to reach for each: `future` when *you* supply the work; `promise` when *someone else* supplies the value
Timestamps:
0:00 - Intro
0:15 - Preview: one-shot async values
0:51 - The code — slow-double helper + main
1:12 - Start the REPL, load the namespace
1:34 - (future ...) returns immediately
1:41 - The future object — status + val
1:48 - @f — already 42
1:56 - (realized? f) — true
2:03 - Three futures in parallel
2:21 - (map deref fs) — (6 2 10), input order
2:29 - (promise) + (realized? p) — false
2:43 - A future delivers from a background thread
2:51 - @p blocks then returns the delivered value
3:09 - Control-D
3:25 - clj -M:run — full demo
3:34 - Recap
4:12 - What's next: Episode 29
Key Takeaways:
1. `future` is for work you're launching now — you know what the computation is, just want it off the main thread.
2. `promise` is for work where the computation isn't the focus — you need a handoff point between threads.
3. Both use `@` to deref. Both block the caller until the value is ready. Both are one-shot.
4. `future` caches the result after the body completes — the first `@f` blocks, subsequent `@f` calls are instant.
5. Parallel `(map deref fs)` preserves input order, not finish order. That's almost always what you want.
6. `realized?` is your non-blocking peek — useful for polling or conditional waits.
Phase 5 continues. Next up: `core.async` — channels and go blocks for structured async coordination.
Taught by CelesteAI. Like and subscribe for more Clojure tutorials!