Browse Learn Clojure Foundations as a Java Developer

Running Asynchronous State Tasks with Agents

Use agents when one in-process state value should receive serialized asynchronous actions, and choose send or send-off based on whether the work is CPU-bound or may block.

Agents are Clojure references for asynchronous state transitions. You send a function to an agent; Clojure applies that function later to the agent’s current state, and the function’s return value becomes the next state.

For Java engineers, an agent is not a general actor framework. It is closer to a small serialized worker attached to one immutable state value.

1(def events (agent []))
2
3(send events conj {:type :login
4                   :user/id 42})

Use this section to decide when agents fit, how to read their state, how failures behave, and when a queue, executor, atom, ref, or database is a better boundary.

In this section

  • Create Agents and Send Actions
    Create Clojure agents for one independent asynchronous state value, send action functions with send or send-off, and keep each action focused on producing the next state.
  • Read Agent State Without Lying to Yourself
    Read an agent as a completed-state snapshot, use await only when blocking is intentional, and avoid designs that need immediate results from asynchronous actions.
  • Handle Agent Failures Explicitly
    Treat agent errors as operational state: inspect failures with agent-error, decide whether to restart, and avoid hiding exceptions inside asynchronous state updates.
  • Choose Practical Agent Use Cases
    Use agents for practical asynchronous state ownership such as telemetry aggregation or ordered local side-effect coordination, while choosing queues, executors, or databases for broader task systems.
Revised on Saturday, May 23, 2026