Composable async tasks with automatic parallelization and grounding.
Composable async tasks with automatic parallelization and grounding.
(acquire sem)Acquire a permit from the semaphore, blocking until one is available.
Acquire a permit from the semaphore, blocking until one is available.
(as-cf t)Convert a task to a CompletableFuture.
The returned future completes when the task settles, with the same value, exception, or cancellation status.
Convert a task to a CompletableFuture. The returned future completes when the task settles, with the same value, exception, or cancellation status.
(as-task v)Convert a value to a Task.
Convert a value to a Task. - Task/Promise: returned as-is - CompletableFuture: wrapped, completes when CF completes - Future: wrapped, blocks virtual thread until Future completes - core.async channel (optional): wrapped, completes when channel delivers - Other values: wrapped in an already-completed Task
(await t phase)(await t phase ms-or-dur)Block until a task reaches the specified phase.
Phases (in order): :pending :running :grounding :transforming :writing :settling :quiescent
Returns true when the phase is reached, false if timeout expires. Without timeout, blocks indefinitely until phase is reached.
Args:
t - The task to await
phase - Target phase keyword (e.g., :settling, :quiescent)
ms-or-dur - Optional timeout as milliseconds (long) or java.time.Duration
Must be called from a virtual thread (unless *assert-virtual* is false).
Example:
(await task :settling) ; Block until result available
(await task :quiescent 1000) ; With 1 second timeout
(await task :quiescent (Duration/ofSeconds 5))
Block until a task reaches the specified phase. Phases (in order): `:pending` `:running` `:grounding` `:transforming` `:writing` `:settling` `:quiescent` Returns true when the phase is reached, false if timeout expires. Without timeout, blocks indefinitely until phase is reached. Args: `t` - The task to await `phase` - Target phase keyword (e.g., `:settling`, `:quiescent`) `ms-or-dur` - Optional timeout as milliseconds (long) or `java.time.Duration` Must be called from a virtual thread (unless `*assert-virtual*` is false). Example: ```clojure (await task :settling) ; Block until result available (await task :quiescent 1000) ; With 1 second timeout (await task :quiescent (Duration/ofSeconds 5)) ```
(cancel t)Attempt to cancel a task. Returns a Task[Boolean].
The returned task settles with:
true: This cancel call won the race and successfully cancelled the taskfalse: The task was already settled, or another cancel call won firstThe returned task settles when the target task reaches quiescent phase, regardless of the boolean result. This enables coordination code to wait for complete teardown before proceeding.
Usage:
(cancel task) ; Fire-and-forget: attempt cancellation without blocking
@(cancel task) ; Wait for target to reach quiescent, returns boolean
Throws UnsupportedOperationException if called on a Promise. Promises are
externally controlled - use fail to complete with an error instead.
Attempt to cancel a task. Returns a `Task[Boolean]`. The returned task settles with: - `true`: This cancel call won the race and successfully cancelled the task - `false`: The task was already settled, or another cancel call won first The returned task settles when the target task reaches quiescent phase, regardless of the boolean result. This enables coordination code to wait for complete teardown before proceeding. Usage: ```clojure (cancel task) ; Fire-and-forget: attempt cancellation without blocking @(cancel task) ; Wait for target to reach quiescent, returns boolean ``` Throws `UnsupportedOperationException` if called on a Promise. Promises are externally controlled - use `fail` to complete with an error instead.
(cancelled? t)True if the given task is cancelled.
True if the given task is cancelled.
(catch t f)(catch t err1 f1)(catch t err1 f1 err2 f2)(catch t err1 f1 err2 f2 err3 f3)(catch t err1 f1 err2 f2 err3 f3 err4 f4)(catch t err1 f1 err2 f2 err3 f3 err4 f4 err5 f5)(catch t err1 f1 err2 f2 err3 f3 err4 f4 err5 f5 & pairs)Handle errors from a task with a recovery function.
Arities:
(catch task f) - Handle any Throwable
(catch task Type f) - Handle specific exception type
(catch task Type1 f1 Type2 f2 ...) - Handle multiple types (first match wins)
The recovery function receives the exception and can:
If the task succeeds, the handler is not called and the value passes through. Cancellation always propagates through catch without invoking handlers.
Multi-pair catch provides exclusive-or semantics like try/catch: only one handler runs, and if that handler throws, the exception propagates (not caught by later pairs in the same catch). Use chained catch calls for nesting semantics where a handler's exception can be caught downstream.
Examples:
;; Catch all exceptions
(catch task
(fn [e]
(log/warn "Task failed" e)
:default-value))
;; Catch specific type
(catch task TimeoutException
(fn [e] :timed-out))
;; Multiple types (exclusive-or, like try/catch)
(catch task
IllegalArgumentException (fn [e] :bad-arg)
IOException (fn [e] :io-error)
Throwable (fn [e] :other))
Handle errors from a task with a recovery function.
Arities:
`(catch task f)` - Handle any Throwable
`(catch task Type f)` - Handle specific exception type
`(catch task Type1 f1 Type2 f2 ...)` - Handle multiple types (first match wins)
The recovery function receives the exception and can:
- Return a fallback value (task succeeds with that value)
- Throw a new exception (task fails with new error)
If the task succeeds, the handler is not called and the value passes through.
Cancellation always propagates through catch without invoking handlers.
Multi-pair catch provides exclusive-or semantics like try/catch: only one handler
runs, and if that handler throws, the exception propagates (not caught by later
pairs in the same catch). Use chained catch calls for nesting semantics where
a handler's exception can be caught downstream.
Examples:
```clojure
;; Catch all exceptions
(catch task
(fn [e]
(log/warn "Task failed" e)
:default-value))
;; Catch specific type
(catch task TimeoutException
(fn [e] :timed-out))
;; Multiple types (exclusive-or, like try/catch)
(catch task
IllegalArgumentException (fn [e] :bad-arg)
IOException (fn [e] :io-error)
Throwable (fn [e] :other))
```(catch-cpu t f)(catch-cpu t err1 f1)(catch-cpu t err1 f1 err2 f2)(catch-cpu t err1 f1 err2 f2 err3 f3)(catch-cpu t err1 f1 err2 f2 err3 f3 err4 f4)(catch-cpu t err1 f1 err2 f2 err3 f3 err4 f4 err5 f5)(catch-cpu t err1 f1 err2 f2 err3 f3 err4 f4 err5 f5 & pairs)Handle errors from a task with a recovery function.
Executes on the platform thread pool.
Arities:
(catch-cpu task f) - Handle any Throwable
(catch-cpu task Type f) - Handle specific exception type
(catch-cpu task Type1 f1 Type2 f2 ...) - Handle multiple types (first match wins)
The recovery function receives the exception and can:
If the task succeeds, the handler is not called and the value passes through. Cancellation always propagates through catch without invoking handlers.
Multi-pair catch provides exclusive-or semantics like try/catch: only one handler runs, and if that handler throws, the exception propagates (not caught by later pairs in the same catch). Use chained catch calls for nesting semantics where a handler's exception can be caught downstream.
Examples:
;; Catch all exceptions
(catch-cpu task
(fn [e]
(log/warn "Task failed" e)
:default-value))
;; Catch specific type
(catch-cpu task TimeoutException
(fn [e] :timed-out))
;; Multiple types (exclusive-or, like try/catch)
(catch-cpu task
IllegalArgumentException (fn [e] :bad-arg)
IOException (fn [e] :io-error)
Throwable (fn [e] :other))
Handle errors from a task with a recovery function.
Executes on the platform thread pool.
Arities:
`(catch-cpu task f)` - Handle any Throwable
`(catch-cpu task Type f)` - Handle specific exception type
`(catch-cpu task Type1 f1 Type2 f2 ...)` - Handle multiple types (first match wins)
The recovery function receives the exception and can:
- Return a fallback value (task succeeds with that value)
- Throw a new exception (task fails with new error)
If the task succeeds, the handler is not called and the value passes through.
Cancellation always propagates through catch without invoking handlers.
Multi-pair catch provides exclusive-or semantics like try/catch: only one handler
runs, and if that handler throws, the exception propagates (not caught by later
pairs in the same catch). Use chained catch calls for nesting semantics where
a handler's exception can be caught downstream.
Examples:
```clojure
;; Catch all exceptions
(catch-cpu task
(fn [e]
(log/warn "Task failed" e)
:default-value))
;; Catch specific type
(catch-cpu task TimeoutException
(fn [e] :timed-out))
;; Multiple types (exclusive-or, like try/catch)
(catch-cpu task
IllegalArgumentException (fn [e] :bad-arg)
IOException (fn [e] :io-error)
Throwable (fn [e] :other))
```(compel t)Wrap a task to make it immune to cascading cancellation.
Returns a compelled wrapper task that observes the inner task. The wrapper:
If the task is created inline, it's also compelled. If it's pre-existing, only the wrapper is compelled, but direct cancel on the wrapper still propagates to the inner task.
Throws IllegalArgumentException if given a Promise. Promises are externally
controlled and compelling them would block teardown on something you don't
control, possibly leading to resource leaks.
Example:
(let [slow-task (compel (s3/PUT large-file))]
;; Parent cascade-cancelled -> slow-task ignores it
;; Direct cancel on slow-task -> cancels the S3 PUT
slow-task)
Wrap a task to make it immune to cascading cancellation. Returns a compelled wrapper task that observes the inner task. The wrapper: - Ignores cascade cancellation from parent contexts - Propagates direct cancellation to the inner task - Creates a 'moat' that blocks cascade but allows direct cancel If the task is created inline, it's also compelled. If it's pre-existing, only the wrapper is compelled, but direct cancel on the wrapper still propagates to the inner task. Throws `IllegalArgumentException` if given a Promise. Promises are externally controlled and compelling them would block teardown on something you don't control, possibly leading to resource leaks. Example: ```clojure (let [slow-task (compel (s3/PUT large-file))] ;; Parent cascade-cancelled -> slow-task ignores it ;; Direct cancel on slow-task -> cancels the S3 PUT slow-task) ```
(comply-interrupt)If interruption has been requested in the current thread,
throw an InterruptedException.
If interruption has been requested in the current thread, throw an `InterruptedException`.
(conditionally-wait ms-or-dur)Sleep for the specified duration, if positive.
Accepts milliseconds (positive number) or java.time.Duration (positive). Zero, negative, nil, and other values are no-ops.
Avoids unnecessary scheduler interaction when no wait is needed.
Sleep for the specified duration, if positive. Accepts milliseconds (positive number) or java.time.Duration (positive). Zero, negative, nil, and other values are no-ops. Avoids unnecessary scheduler interaction when no wait is needed.
(cpu-task & body)Execute a CPU-bound task on the platform thread pool. Use this for compute-intensive work without blocking operations.
Execute a CPU-bound task on the platform thread pool. Use this for compute-intensive work without blocking operations.
(deref-cpu t)Temporarily bypass the restriction that Tasks can't be parked on platform threads.
Temporarily bypass the restriction that Tasks can't be parked on platform threads.
(done t f)Run a side-effecting function when a task completes with success or error.
The function receives [value error]:
[value nil] (value may be nil)[nil exception]The return value is ignored - the original task result passes through unchanged.
If f throws, that exception fails the task chain.
This is the side-effecting counterpart to handle:
handle: runs on success/error, returns transformed valuedone: runs on success/error, passes through original resultCancellation: Does NOT run when task is cancelled. Use finally for
cleanup that must run on cancellation.
Run a side-effecting function when a task completes with success or error. The function receives `[value error]`: - On success: `[value nil]` (value may be nil) - On error: `[nil exception]` The return value is ignored - the original task result passes through unchanged. If `f` throws, that exception fails the task chain. This is the side-effecting counterpart to `handle`: - `handle`: runs on success/error, returns transformed value - `done`: runs on success/error, passes through original result **Cancellation**: Does NOT run when task is cancelled. Use `finally` for cleanup that must run on cancellation.
(done-cpu t f)Run a side-effecting function when a task completes with success or error.
Executes on the platform thread pool.
The function receives [value error]:
[value nil] (value may be nil)[nil exception]The return value is ignored - the original task result passes through unchanged.
If f throws, that exception fails the task chain.
This is the side-effecting counterpart to handle:
handle: runs on success/error, returns transformed valuedone: runs on success/error, passes through original resultCancellation: Does NOT run when task is cancelled. Use finally for
cleanup that must run on cancellation.
Run a side-effecting function when a task completes with success or error. Executes on the platform thread pool. The function receives `[value error]`: - On success: `[value nil]` (value may be nil) - On error: `[nil exception]` The return value is ignored - the original task result passes through unchanged. If `f` throws, that exception fails the task chain. This is the side-effecting counterpart to `handle`: - `handle`: runs on success/error, returns transformed value - `done`: runs on success/error, passes through original result **Cancellation**: Does NOT run when task is cancelled. Use `finally` for cleanup that must run on cancellation.
(err t f)Run a side-effecting function when a task fails. Mirror of ok.
Calls (f exception) when the task completes with an error.
Successful tasks pass through unchanged without calling f.
The function is for side effects:
f throws, that exception replaces the originalCancellation: Does NOT run when task is cancelled. Cancellation is a control
signal, not an error. Use finally for cleanup that must run on cancellation.
Run a side-effecting function when a task fails. Mirror of `ok`. Calls `(f exception)` when the task completes with an error. Successful tasks pass through unchanged without calling `f`. The function is for side effects: - Return value is ignored (original exception passes through) - If `f` throws, that exception replaces the original **Cancellation**: Does NOT run when task is cancelled. Cancellation is a control signal, not an error. Use `finally` for cleanup that must run on cancellation.
(err-cpu t f)Run a side-effecting function when a task fails. Mirror of ok.
Executes on the platform thread pool.
Calls (f exception) when the task completes with an error.
Successful tasks pass through unchanged without calling f.
The function is for side effects:
f throws, that exception replaces the originalCancellation: Does NOT run when task is cancelled. Cancellation is a control
signal, not an error. Use finally for cleanup that must run on cancellation.
Run a side-effecting function when a task fails. Mirror of `ok`. Executes on the platform thread pool. Calls `(f exception)` when the task completes with an error. Successful tasks pass through unchanged without calling `f`. The function is for side effects: - Return value is ignored (original exception passes through) - If `f` throws, that exception replaces the original **Cancellation**: Does NOT run when task is cancelled. Cancellation is a control signal, not an error. Use `finally` for cleanup that must run on cancellation.
(exceptional? t)Returns true if the task has completed with an exception. Returns false if task is not completed or completed successfully.
Analogous to CompletableFuture.isCompletedExceptionally(). Useful for:
Example:
(when (completed-exceptionally? task)
(log/error "Task failed" {:task task}))
Returns true if the task has completed with an exception.
Returns false if task is not completed or completed successfully.
Analogous to `CompletableFuture.isCompletedExceptionally()`. Useful for:
- Checking error state without dereferencing
- Conditional error handling logic
- Metrics and monitoring
Example:
```clojure
(when (completed-exceptionally? task)
(log/error "Task failed" {:task task}))
```(fail p e)Complete a promise with an exception.
Primarily useful for completing promises when bridging callback-based APIs that have separate success/error paths.
Example:
(let [p (promise)]
(some-callback-api
{:on-success (fn [result] (p result))
:on-error (fn [error] (fail p error))})
p)
Complete a promise with an exception.
Primarily useful for completing promises when bridging callback-based APIs
that have separate success/error paths.
Example:
```clojure
(let [p (promise)]
(some-callback-api
{:on-success (fn [result] (p result))
:on-error (fn [error] (fail p error))})
p)
```(failed-task e)Create a task that is already completed with an exception.
Analogous to CompletableFuture.failedFuture(). Useful for:
Example:
(if valid?
(fetch-data)
(failed-task (ex-info "Invalid input" {:code 400})))
Create a task that is already completed with an exception.
Analogous to `CompletableFuture.failedFuture()`. Useful for:
- Short-circuiting with known errors
- Testing error handling paths
- Returning failed tasks from conditional logic
Example:
```clojure
(if valid?
(fetch-data)
(failed-task (ex-info "Invalid input" {:code 400})))
```(finally t f)Run a function after a task completes, regardless of outcome.
The function receives [value error cancelled?]:
[value nil false] (value may be nil)[nil exception false][nil CancellationException true]The return value is ignored - the original task result passes through unchanged. Exceptions from the function propagate and fail the task chain.
Cancellation: finally is the ONLY handler guaranteed to run when a task is
cancelled. All other handlers (ok, err, done, catch, handle, then) skip
execution on cancellation. Use finally when you need code to run regardless
of how the task ends—especially for resource cleanup.
Example:
(finally task
(fn [v e cancelled]
(release-resource)
(when cancelled
(log/info "Task was cancelled"))))
Run a function after a task completes, regardless of outcome.
The function receives `[value error cancelled?]`:
- On success: `[value nil false]` (value may be nil)
- On error: `[nil exception false]`
- On cancellation: `[nil CancellationException true]`
The return value is ignored - the original task result passes through unchanged.
Exceptions from the function propagate and fail the task chain.
**Cancellation**: `finally` is the ONLY handler guaranteed to run when a task is
cancelled. All other handlers (`ok`, `err`, `done`, `catch`, `handle`, `then`) skip
execution on cancellation. Use `finally` when you need code to run regardless
of how the task ends—especially for resource cleanup.
Example:
```clojure
(finally task
(fn [v e cancelled]
(release-resource)
(when cancelled
(log/info "Task was cancelled"))))
```(finally-cpu t f)Run a function after a task completes, regardless of outcome.
Executes on the platform thread pool.
The function receives [value error cancelled?]:
[value nil false] (value may be nil)[nil exception false][nil CancellationException true]The return value is ignored - the original task result passes through unchanged. Exceptions from the function propagate and fail the task chain.
Cancellation: finally is the ONLY handler guaranteed to run when a task is
cancelled. All other handlers (ok, err, done, catch, handle, then) skip
execution on cancellation. Use finally when you need code to run regardless
of how the task ends—especially for resource cleanup.
Example:
(finally-cpu task
(fn [v e cancelled]
(release-resource)
(when cancelled
(log/info "Task was cancelled"))))
Run a function after a task completes, regardless of outcome.
Executes on the platform thread pool.
The function receives `[value error cancelled?]`:
- On success: `[value nil false]` (value may be nil)
- On error: `[nil exception false]`
- On cancellation: `[nil CancellationException true]`
The return value is ignored - the original task result passes through unchanged.
Exceptions from the function propagate and fail the task chain.
**Cancellation**: `finally` is the ONLY handler guaranteed to run when a task is
cancelled. All other handlers (`ok`, `err`, `done`, `catch`, `handle`, `then`) skip
execution on cancellation. Use `finally` when you need code to run regardless
of how the task ends—especially for resource cleanup.
Example:
```clojure
(finally-cpu task
(fn [v e cancelled]
(release-resource)
(when cancelled
(log/info "Task was cancelled"))))
```(get-now t default)Get the current value if completed, otherwise return default. Non-blocking alternative to deref. Throws if completed exceptionally.
Analogous to CompletableFuture.getNow(). Useful for:
Example:
(let [result (get-now task :not-ready)]
(if (= result :not-ready)
(log/info "Still waiting...")
(process result)))
Get the current value if completed, otherwise return default.
Non-blocking alternative to deref. Throws if completed exceptionally.
Analogous to `CompletableFuture.getNow()`. Useful for:
- Polling task status without blocking
- Optimistic reads in hot paths
- Conditional logic based on completion
Example:
```clojure
(let [result (get-now task :not-ready)]
(if (= result :not-ready)
(log/info "Still waiting...")
(process result)))
```(get-state t)Return a map of debugging information about a task.
Return a map of debugging information about a task.
(handle t f)Handle both success and error cases with a single function.
The function receives [value error]:
[value nil] (value may be nil)[nil exception]Returns the result of calling the function.
This is more fundamental than done - use when you need to return
a different value based on success/error.
Cancellation: Does NOT run when task is cancelled. Use finally for
cleanup that must run on cancellation.
Example:
(handle task
(fn [v e]
(if e
(log-and-return-default e)
(process v))))
Handle both success and error cases with a single function.
The function receives `[value error]`:
- On success: `[value nil]` (value may be nil)
- On error: `[nil exception]`
Returns the result of calling the function.
This is more fundamental than `done` - use when you need to return
a different value based on success/error.
**Cancellation**: Does NOT run when task is cancelled. Use `finally` for
cleanup that must run on cancellation.
Example:
```clojure
(handle task
(fn [v e]
(if e
(log-and-return-default e)
(process v))))
```(handle-cpu t f)Handle both success and error cases with a single function.
Executes on the platform thread pool.
The function receives [value error]:
[value nil] (value may be nil)[nil exception]Returns the result of calling the function.
This is more fundamental than done - use when you need to return
a different value based on success/error.
Cancellation: Does NOT run when task is cancelled. Use finally for
cleanup that must run on cancellation.
Example:
(handle-cpu task
(fn [v e]
(if e
(log-and-return-default e)
(process v))))
Handle both success and error cases with a single function.
Executes on the platform thread pool.
The function receives `[value error]`:
- On success: `[value nil]` (value may be nil)
- On error: `[nil exception]`
Returns the result of calling the function.
This is more fundamental than `done` - use when you need to return
a different value based on success/error.
**Cancellation**: Does NOT run when task is cancelled. Use `finally` for
cleanup that must run on cancellation.
Example:
```clojure
(handle-cpu task
(fn [v e]
(if e
(log-and-return-default e)
(process v))))
```(if-qlet bindings pos-case neg-case)Async if-let for tasks. Awaits test expression, and if truthy, binds its value to form.
Like if-let, but the test expression can be a task. Awaits the test result,
and if truthy, binds that value to the binding form and evaluates the then clause.
Otherwise evaluates the else clause.
Returns a task.
Syntax: (if-qlet [binding-form test-expr] then-expr else-expr)
Example:
(if-qlet [user (fetch-user id)]
(process-user user) ; Executes if user is truthy
(handle-not-found)) ; Executes if user is nil/false
Async if-let for tasks. Awaits test expression, and if truthy, binds its value to form. Like `if-let`, but the test expression can be a task. Awaits the test result, and if truthy, binds that value to the binding form and evaluates the then clause. Otherwise evaluates the else clause. Returns a task. Syntax: `(if-qlet [binding-form test-expr] then-expr else-expr)` Example: ```clojure (if-qlet [user (fetch-user id)] (process-user user) ; Executes if user is truthy (handle-not-found)) ; Executes if user is nil/false ```
(interrupted?)(interrupted? thread)Check if the current or specified thread is interrupted.
Check if the current or specified thread is interrupted.
(monitor t ms-or-dur side-effect-fn)Non-destructive monitoring wrapper that observes a task without affecting its outcome.
Unlike timeout, this does NOT race the task or change its result. Instead, it runs
a side-effect function if the task hasn't completed within the specified duration.
The original task continues running and its result is returned unchanged.
This is useful for diagnostics: log warnings when operations are slow without actually timing them out or affecting their execution.
Implementation: Uses compel to create a protected wrapper around the task, then
races that wrapper against a timeout. When the timeout fires, it cancels the wrapper
but not the original task. The watchdog runs 'on the side' and is discarded.
Args:
t Task to monitorms-or-dur Duration after which to trigger side effect:
- Long: milliseconds
- java.time.Duration: duration objectside-effect-fn Function (or value) passed to timeout's default parameter.
If a function, it's called when the timeout fires.
Exceptions from the side effect are caught and logged.Returns the original task unchanged - result and timing are unaffected.
Example:
(-> (slow-operation)
(monitor 5000
#(log/warn "Operation exceeded 5s")))
The side effect runs in a fire-and-forget manner - exceptions are logged but don't affect the monitored task.
Non-destructive monitoring wrapper that observes a task without affecting its outcome.
Unlike `timeout`, this does NOT race the task or change its result. Instead, it runs
a side-effect function if the task hasn't completed within the specified duration.
The original task continues running and its result is returned unchanged.
This is useful for diagnostics: log warnings when operations are slow without
actually timing them out or affecting their execution.
Implementation: Uses `compel` to create a protected wrapper around the task, then
races that wrapper against a timeout. When the timeout fires, it cancels the wrapper
but not the original task. The watchdog runs 'on the side' and is discarded.
Args:
- `t` Task to monitor
- `ms-or-dur` Duration after which to trigger side effect:
- Long: milliseconds
- `java.time.Duration`: duration object
- `side-effect-fn` Function (or value) passed to timeout's default parameter.
If a function, it's called when the timeout fires.
Exceptions from the side effect are caught and logged.
Returns the original task unchanged - result and timing are unaffected.
Example:
```clojure
(-> (slow-operation)
(monitor 5000
#(log/warn "Operation exceeded 5s")))
```
The side effect runs in a fire-and-forget manner - exceptions are logged but don't
affect the monitored task.(ok t f)Run a side-effecting function after a task completes successfully.
Calls (f value) when the task succeeds.
The function is for side effects:
f throws, that exception fails the task chainUse for logging, metrics, or triggering downstream effects on success.
Run a side-effecting function after a task completes successfully. Calls `(f value)` when the task succeeds. The function is for side effects: - Return value is ignored (original task value passes through) - If `f` throws, that exception fails the task chain - Errors and cancellation skip calling the function Use for logging, metrics, or triggering downstream effects on success.
(ok-cpu t f)Run a side-effecting function after a task completes successfully.
Executes on the platform thread pool.
Calls (f value) when the task succeeds.
The function is for side effects:
f throws, that exception fails the task chainUse for logging, metrics, or triggering downstream effects on success.
Run a side-effecting function after a task completes successfully. Executes on the platform thread pool. Calls `(f value)` when the task succeeds. The function is for side effects: - Return value is ignored (original task value passes through) - If `f` throws, that exception fails the task chain - Errors and cancellation skip calling the function Use for logging, metrics, or triggering downstream effects on success.
(promise)Create a Promise with externally controlled resolution.
A Promise is functionally identical to a Task (implements ITask),
but unlike task which executes immediately, a Promise's resolution is controlled
externally.
Chaining operations on promises work as with tasks.
(def p (q/promise))
(deliver p :result) ; Delivers :result to the promise
(p :result) ; also works
Example:
(let [p (q/promise)]
(future (Thread/sleep 1000) (p :done))
(-> p
(then (fn [v] (str "Got: " v)))
(ok println))) ; Prints "Got: done" after 1 second
Create a Promise with externally controlled resolution.
A Promise is functionally identical to a Task (implements `ITask`),
but unlike `task` which executes immediately, a Promise's resolution is controlled
externally.
Chaining operations on promises work as with tasks.
```clojure
(def p (q/promise))
(deliver p :result) ; Delivers :result to the promise
(p :result) ; also works
```
Example:
```clojure
(let [p (q/promise)]
(future (Thread/sleep 1000) (p :done))
(-> p
(then (fn [v] (str "Got: " v)))
(ok println))) ; Prints "Got: done" after 1 second
```(promise? p)Returns true if p is a Promise, false otherwise.
Returns true if p is a Promise, false otherwise.
(q & body)Execute the body in the current thread, then return a task containing the return value.
If the return value is a regular clojure data structure that contains tasks, awaits the completion of all contained tasks before becoming realized.
Execute the body in _the current thread_, then return a task containing the return value. If the return value is a regular clojure data structure that contains tasks, awaits the completion of all contained tasks before becoming realized.
(qdo)(qdo t)(qdo t1 t2)(qdo t1 t2 t3)(qdo t1 t2 t3 t4)(qdo t1 t2 t3 t4 t5)(qdo t1 t2 t3 t4 t5 t6)(qdo t1 t2 t3 t4 t5 t6 t7)(qdo t1 t2 t3 t4 t5 t6 t7 t8)(qdo t1 t2 t3 t4 t5 t6 t7 t8 t9)(qdo t1 t2 t3 t4 t5 t6 t7 t8 t9 t10)(qdo t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 & ts)Await all tasks, but only return the output of the last one.
If one task throws, all are cancelled.
Await all tasks, but only return the output of the last one. If one task throws, all are cancelled.
(qfor [bind-to coll] & body)Map over a collection eagerly, executing body for each element.
Returns a Task containing a vector of results. If the body returns tasks, they execute in parallel and are awaited concurrently.
Does not automatically wrap the body in a task.
Unlike for, does not support :let, :when, or :while modifiers.
Example:
@(qfor [id user-ids]
(fetch-user id))
;; => [{:id 1 ...} {:id 2 ...} ...]
Map over a collection eagerly, executing body for each element.
Returns a Task containing a vector of results. If the body returns tasks,
they execute in parallel and are awaited concurrently.
Does not automatically wrap the body in a task.
Unlike `for`, does not support `:let`, `:when`, or `:while` modifiers.
Example:
```clojure
@(qfor [id user-ids]
(fetch-user id))
;; => [{:id 1 ...} {:id 2 ...} ...]
```(qlet bindings & body)Async let with automatic dependency analysis and parallel execution.
Like let, but analyzes dependencies between bindings and executes
independent bindings in parallel using Quiescent.
Returns a Task (not dereferenced) - use @ to get the value or chain with q/then.
Example:
(q/qlet [user (fetch-user id) ; Starts immediately
posts (fetch-posts user-id) ; Parallel with user fetch
profile (process-user user) ; Waits for user
result (combine profile posts)] ; Waits for both
result)
Bindings are NOT wrapped in tasks automatically - use q/task explicitly if needed:
(q/qlet [data {:a 1 :b 2} ; Plain value, no thread
slow (q/task (slow-fn))] ; Explicit task
...)
Async let with automatic dependency analysis and parallel execution.
Like `let`, but analyzes dependencies between bindings and executes
independent bindings in parallel using Quiescent.
Returns a Task (not dereferenced) - use `@` to get the value or chain with `q/then`.
Example:
```clojure
(q/qlet [user (fetch-user id) ; Starts immediately
posts (fetch-posts user-id) ; Parallel with user fetch
profile (process-user user) ; Waits for user
result (combine profile posts)] ; Waits for both
result)
```
Bindings are NOT wrapped in tasks automatically - use `q/task` explicitly if needed:
```clojure
(q/qlet [data {:a 1 :b 2} ; Plain value, no thread
slow (q/task (slow-fn))] ; Explicit task
...)
```(qmerge)(qmerge m)(qmerge m1 m2)(qmerge m1 m2 m3)(qmerge m1 m2 m3 m4)(qmerge m1 m2 m3 m4 m5)(qmerge m1 m2 m3 m4 m5 m6)(qmerge m1 m2 m3 m4 m5 m6 m7)(qmerge m1 m2 m3 m4 m5 m6 m7 m8)(qmerge m1 m2 m3 m4 m5 m6 m7 m8 m9)(qmerge m1 m2 m3 m4 m5 m6 m7 m8 m9 m10)(qmerge m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 & ms)Takes a map where the values may be tasks. If multiple maps are given, merges them.
Returns a Task containing a map with all nested tasks resolved to their values.
Prefer this over (then task-a task-b merge) - it skips the grounding phase
since the result is known to contain only resolved values.
Takes a map where the values may be tasks. If multiple maps are given, merges them. Returns a Task containing a map with all nested tasks resolved to their values. Prefer this over `(then task-a task-b merge)` - it skips the grounding phase since the result is known to contain only resolved values.
(race & tasks)Race multiple tasks, returning the first successful result.
Returns a Task that completes with the value of whichever task settles first with a non-exceptional result. Losing tasks are cancelled (unless compelled).
If all tasks fail, returns an exception. If one task fails, returns the combined errors as ex-info with :errors key.
Race multiple tasks, returning the first successful result. Returns a Task that completes with the value of whichever task settles first with a non-exceptional result. Losing tasks are cancelled (unless compelled). If all tasks fail, returns an exception. If one task fails, returns the combined errors as ex-info with :errors key.
(race-stateful release & tasks)Race tasks that produce stateful resources, with cleanup for losers.
Like race, but handles the edge case where multiple tasks complete
simultaneously. When two tasks both produce a value before a winner is
determined, one wins and the other's value is passed to release.
Args:
release - Function called with each orphaned value (realized but lost)
tasks - Tasks to race
Example:
(race-stateful #(.close %) alloc-a alloc-b alloc-c)
Returns a Task that completes with the first successful result. Losing tasks are cancelled. If all tasks fail, returns the combined errors.
Race tasks that produce stateful resources, with cleanup for losers. Like `race`, but handles the edge case where multiple tasks complete simultaneously. When two tasks both produce a value before a winner is determined, one wins and the other's value is passed to `release`. Args: `release` - Function called with each orphaned value (realized but lost) `tasks` - Tasks to race Example: ```clojure (race-stateful #(.close %) alloc-a alloc-b alloc-c) ``` Returns a Task that completes with the first successful result. Losing tasks are cancelled. If all tasks fail, returns the combined errors.
(release sem)Release a permit back to the semaphore.
Release a permit back to the semaphore.
(retry f)(retry f
{:keys [retries backoff-ms backoff-factor retry-callback validate]
:or {retries 3
backoff-ms 2000
backoff-factor 2
validate default-validate
retry-callback (constantly nil)}
:as args})Takes a function that returns a task. Tries to rerun that task until it succeeds or runs out of retries, according to the options given.
f is a function that takes one argument: a bool that is false for the first
attempt, and true for any subsequent attempts.
Fails immediately without retries if f throws (i.e., if task construction fails).
If validate is given, it will be run on the result with [value exception], and
retries will issue depending on if validate throws. If validate does not throw,
its return value is considered viable.
Takes a function that returns a task. Tries to rerun that task until it succeeds or runs out of retries, according to the options given. `f` is a function that takes one argument: a bool that is `false` for the first attempt, and `true` for any subsequent attempts. Fails immediately without retries if `f` throws (i.e., if task construction fails). If `validate` is given, it will be run on the result with `[value exception]`, and retries will issue depending on if `validate` throws. If `validate` does not throw, its return value is considered viable.
(semaphore n)(semaphore n fair)Creates a semaphore. By default, fairness is set to true, which
means that allocations will be first-in first-out.
Creates a semaphore. By default, `fairness` is set to true, which means that allocations will be first-in first-out.
(sleep ms-or-duration)(sleep ms-or-duration default)Create a task that sleeps for the specified duration, then returns default.
Args:
ms-or-duration - Sleep duration as either:
- Long: non-negative milliseconds (0 = immediate return)
- java.time.Duration: non-negative duration
default - Optional value to return after sleep (default: nil)
- If a function, calls it and returns its result
- If a Throwable, fails the task with that exception
Returns a task that completes after the specified duration. Throws for negative or unsupported duration values.
Example:
@(sleep 100) ; Sleep 100ms, return nil
@(sleep 100 :done) ; Sleep 100ms, return :done
@(sleep (Duration/ofSeconds 1)) ; Sleep 1 second, return nil
@(sleep 100 (Exception. "Boom")) ; Sleep 100ms, then throw
Create a task that sleeps for the specified duration, then returns `default`.
Args:
`ms-or-duration` - Sleep duration as either:
- Long: non-negative milliseconds (0 = immediate return)
- `java.time.Duration`: non-negative duration
`default` - Optional value to return after sleep (default: nil)
- If a function, calls it and returns its result
- If a Throwable, fails the task with that exception
Returns a task that completes after the specified duration.
Throws for negative or unsupported duration values.
Example:
```clojure
@(sleep 100) ; Sleep 100ms, return nil
@(sleep 100 :done) ; Sleep 100ms, return :done
@(sleep (Duration/ofSeconds 1)) ; Sleep 1 second, return nil
@(sleep 100 (Exception. "Boom")) ; Sleep 100ms, then throw
```(task & body)Create and run a task on the virtual thread executor. Returns the Task immediately; body executes asynchronously. Deref (@) blocks until the task settles and returns the result or throws.
Create and run a task on the virtual thread executor. Returns the Task immediately; body executes asynchronously. Deref (@) blocks until the task settles and returns the result or throws.
(task? v)Returns true if v is a Task, false otherwise.
Note: Returns false for Promises. Use (satisfies? ITask v) to check for both.
Returns true if v is a Task, false otherwise. Note: Returns false for Promises. Use `(satisfies? ITask v)` to check for both.
(taskable? v)Returns true if v can be converted to a Task via as-task.
True for: Task, Promise, CompletableFuture, Future, core.async channel. False for: nil, plain values, collections.
Returns true if v can be converted to a Task via `as-task`. True for: Task, Promise, CompletableFuture, Future, core.async channel. False for: nil, plain values, collections.
(then t f)(then t1 t2 f)(then t1 t2 t3 f)(then t1 t2 t3 t4 f)(then t1 t2 t3 t4 t5 f)(then t1 t2 t3 t4 t5 t6 f)(then t1 t2 t3 t4 t5 t6 t7 f)(then t1 t2 t3 t4 t5 t6 t7 t8 f)(then t1 t2 t3 t4 t5 t6 t7 t8 t9 f)(then t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 f)(then t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 & more)Chain a function after one or more tasks complete.
Single task:
(then task f) - Calls (f @task)
Multiple tasks:
(then t1 t2 t3 f) - Waits for all tasks in parallel, calls (f @t1 @t2 @t3)
Chain a function after one or more tasks complete. Single task: `(then task f)` - Calls `(f @task)` Multiple tasks: `(then t1 t2 t3 f)` - Waits for all tasks in parallel, calls `(f @t1 @t2 @t3)`
(then-cpu t f)(then-cpu t1 t2 f)(then-cpu t1 t2 t3 f)(then-cpu t1 t2 t3 t4 f)(then-cpu t1 t2 t3 t4 t5 f)(then-cpu t1 t2 t3 t4 t5 t6 f)(then-cpu t1 t2 t3 t4 t5 t6 t7 f)(then-cpu t1 t2 t3 t4 t5 t6 t7 t8 f)(then-cpu t1 t2 t3 t4 t5 t6 t7 t8 t9 f)(then-cpu t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 f)(then-cpu t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 & more)Chain a function after one or more tasks complete.
Executes on the platform thread pool.
Single task:
(then task f) - Calls (f @task)
Multiple tasks:
(then t1 t2 t3 f) - Waits for all tasks in parallel, calls (f @t1 @t2 @t3)
Chain a function after one or more tasks complete. Executes on the platform thread pool. Single task: `(then task f)` - Calls `(f @task)` Multiple tasks: `(then t1 t2 t3 f)` - Waits for all tasks in parallel, calls `(f @t1 @t2 @t3)`
(throw-on-platform-park! bool)Configure whether awaiting from a platform thread throws an exception. When true (default), parking a platform thread raises IllegalStateException. Set to false for testing or when platform thread parking is intentional.
Configure whether awaiting from a platform thread throws an exception. When true (default), parking a platform thread raises IllegalStateException. Set to false for testing or when platform thread parking is intentional.
(timeout t ms-or-dur)(timeout t ms-or-dur default)Asynchronous timeout that races a task against a sleep timer.
Roughly equivalent to (deref task ms-or-dur default), except:
default is optional. If omitted, throws a TimeoutException on timeout.
Args:
t - Task to race against timeout
ms-or-dur - Timeout duration as either:
- Long: milliseconds
- java.time.Duration: duration object
default - Optional value/behavior on timeout:
- Value: returned on timeout
- Function: executed and its result returned
- Exception: thrown on timeout
- Omitted: throws TimeoutException
Returns a task that completes with either the task's result or the timeout default.
Example:
@(timeout my-task 1000) ; Throw TimeoutException after 1s
@(timeout my-task 1000 :timed-out) ; Return :timed-out after 1s
@(timeout my-task (Duration/ofSeconds 5)) ; Throw after 5 seconds
Asynchronous timeout that races a task against a sleep timer.
Roughly equivalent to `(deref task ms-or-dur default)`, except:
- Asynchronous: doesn't block the calling thread unless dereferenced. Returns a task.
- `default` is optional. If omitted, throws a `TimeoutException` on timeout.
- If a function is given, executes it on timeout, and returns its result.
- If an exception is given, throws it on timeout.
Args:
`t` - Task to race against timeout
`ms-or-dur` - Timeout duration as either:
- Long: milliseconds
- `java.time.Duration`: duration object
`default` - Optional value/behavior on timeout:
- Value: returned on timeout
- Function: executed and its result returned
- Exception: thrown on timeout
- Omitted: throws `TimeoutException`
Returns a task that completes with either the task's result or the timeout default.
Example:
```clojure
@(timeout my-task 1000) ; Throw TimeoutException after 1s
@(timeout my-task 1000 :timed-out) ; Return :timed-out after 1s
@(timeout my-task (Duration/ofSeconds 5)) ; Throw after 5 seconds
```(when-qlet bindings & body)Async when-let for tasks. Awaits test expression, and if truthy, binds its value to form.
Like when-let, but the test expression can be a task. Awaits the test result,
and if truthy, binds that value to the binding form and evaluates the body expressions.
Returns nil if the test is falsy.
Returns a task.
Syntax: (when-qlet [binding-form test-expr] body-expr*)
Example:
(when-qlet [user (fetch-user id)]
(log/info "Processing user" user)
(process-user user)) ; Executes if user is truthy, returns nil otherwise
Async when-let for tasks. Awaits test expression, and if truthy, binds its value to form. Like `when-let`, but the test expression can be a task. Awaits the test result, and if truthy, binds that value to the binding form and evaluates the body expressions. Returns nil if the test is falsy. Returns a task. Syntax: `(when-qlet [binding-form test-expr] body-expr*)` Example: ```clojure (when-qlet [user (fetch-user id)] (log/info "Processing user" user) (process-user user)) ; Executes if user is truthy, returns nil otherwise ```
(with-semaphore sem & body)Execute body while holding a semaphore permit. Releases on completion or exception.
Execute body while holding a semaphore permit. Releases on completion or exception.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |