Composable async tasks with automatic parallelization and grounding.
Composable async tasks with automatic parallelization and grounding.
(abort-signal)Create and return an AbortSignal tied to the current task's lifecycle.
Each invocation creates a new AbortController and returns its signal. The signal is aborted when the task settles (completes, fails, or is cancelled).
Example:
(q (js/fetch url #js {:signal (abort-signal)}))
;; If the task is cancelled, completes, or fails, the fetch will abort.
Throws if called outside a task scope.
Create and return an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
tied to the current task's lifecycle.
Each invocation creates a new AbortController and returns its signal. The signal
is aborted when the task settles (completes, fails, or is cancelled).
Example:
```clojure
(q (js/fetch url #js {:signal (abort-signal)}))
;; If the task is cancelled, completes, or fails, the fetch will abort.
```
Throws if called outside a task scope.(aborted? signal)Returns true if the abort signal has been triggered.
Returns true if the abort signal has been triggered.
(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.
Structured concurrency boundary: CompletableFutures do not participate in Quiescent's structured concurrency. Continuations attached to the returned CF (via .thenApply, .thenCompose, etc.) run detached from any task lineage - effectively at root level. Tasks created within those continuations will start their own independent structured concurrency trees.
Convert a task to a CompletableFuture. The returned future completes when the task settles, with the same value, exception, or cancellation status. Structured concurrency boundary: CompletableFutures do not participate in Quiescent's structured concurrency. Continuations attached to the returned CF (via .thenApply, .thenCompose, etc.) run detached from any task lineage - effectively at root level. Tasks created within those continuations will start their own independent structured concurrency trees.
(as-jsp t)Convert a task to a JavaScript Promise.
The returned promise resolves when the task settles, with the same value or rejection. Note: JS Promises cannot represent cancellation distinctly - a cancelled task will reject with the cancellation error.
Structured concurrency boundary: JavaScript Promises do not participate in Quiescent's structured concurrency. Continuations attached to the returned Promise (via .then, .catch, etc.) run detached from any task lineage - effectively at root level. Tasks created within those continuations will start their own independent structured concurrency trees.
Convert a task to a JavaScript Promise. The returned promise resolves when the task settles, with the same value or rejection. Note: JS Promises cannot represent cancellation distinctly - a cancelled task will reject with the cancellation error. Structured concurrency boundary: JavaScript Promises do not participate in Quiescent's structured concurrency. Continuations attached to the returned Promise (via .then, .catch, etc.) run detached from any task lineage - effectively at root level. Tasks created within those continuations will start their own independent structured concurrency trees.
(as-task v)Convert an async primitive to a Task.
Supports CompletableFuture (CLJ), Future (CLJ), js/Promise (CLJS), and core.async channels (when adapter is loaded). Tasks and Promises pass through unchanged. Plain values are wrapped in an immediately-settled task.
Use this to bridge external async APIs into Quiescent's task model.
Convert an async primitive to a Task. Supports CompletableFuture (CLJ), Future (CLJ), js/Promise (CLJS), and core.async channels (when adapter is loaded). Tasks and Promises pass through unchanged. Plain values are wrapped in an immediately-settled task. Use this to bridge external async APIs into Quiescent's task model.
(await t)(await t ms-or-dur)Re until a task has settled (has a result available).
Returns true when settled, false if timeout expires. Without timeout, blocks indefinitely.
Unlike deref, does not throw if the task failed or was cancelled.
Args:
t The task to awaitms-or-dur Optional timeout as milliseconds (long) or java.time.DurationExample:
(await task) ; Block until settled
(await task 1000) ; With 1 second timeout
(await task (Duration/ofSeconds 5)) ; With Duration timeout
Re until a task has settled (has a result available). Returns true when settled, false if timeout expires. Without timeout, blocks indefinitely. Unlike `deref`, does not throw if the task failed or was cancelled. Args: - `t` The task to await - `ms-or-dur` Optional timeout as milliseconds (long) or `java.time.Duration` Example: ```clojure (await task) ; Block until settled (await task 1000) ; With 1 second timeout (await task (Duration/ofSeconds 5)) ; With Duration timeout ```
(cancel t)Attempt to cancel a task, promise, or gate. Returns a Task[Boolean].
The returned task settles with:
true: This cancel call won the race and successfully cancelled the targetfalse: The target was already settled, or another cancel call won firstThe returned task settles when the target reaches quiescent phase, regardless of the boolean result. This enables coordination code to wait for complete teardown before proceeding.
Cancellation propagates to chained tasks as cancellation (not failure),
meaning cancelled? will return true and only finally handlers run.
Usage:
(cancel task) ; Fire-and-forget: attempt cancellation without blocking
@(cancel task) ; Wait for target to reach quiescent, returns boolean
Attempt to cancel a task, promise, or gate. Returns a `Task[Boolean]`. The returned task settles with: - `true`: This cancel call won the race and successfully cancelled the target - `false`: The target was already settled, or another cancel call won first The returned task settles when the target reaches quiescent phase, regardless of the boolean result. This enables coordination code to wait for complete teardown before proceeding. Cancellation propagates to chained tasks as cancellation (not failure), meaning `cancelled?` will return true and only `finally` handlers run. Usage: ```clojure (cancel task) ; Fire-and-forget: attempt cancellation without blocking @(cancel task) ; Wait for target to reach quiescent, returns boolean ```
(cancelled? t)True if the given task is cancelled.
True if the given task is cancelled.
(catch t f)(catch t e f)(catch t e1 f1 e2 f2)(catch t e1 f1 e2 f2 e3 f3)(catch t e1 f1 e2 f2 e3 f3 e4 f4)(catch t e1 f1 e2 f2 e3 f3 e4 f4 e5 f5)(catch t e1 f1 e2 f2 e3 f3 e4 f4 e5 f5 & pairs)Handle errors from a task with a recovery function.
Arities:
(catch task f) - Handle any exception
(catch task Type f) - Handle specific exception type (CLJ) or matching predicate (CLJS)
(catch task Type1 f1 Type2 f2 ...) - Handle multiple types/predicates (first match wins)
In CLJ, pass exception classes (e.g., IllegalArgumentException).
In CLJS, pass predicates (e.g., #(= :bad-arg (:type (ex-data %)))).
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 exception
`(catch task Type f)` - Handle specific exception type (CLJ) or matching predicate (CLJS)
`(catch task Type1 f1 Type2 f2 ...)` - Handle multiple types/predicates (first match wins)
In CLJ, pass exception classes (e.g., `IllegalArgumentException`).
In CLJS, pass predicates (e.g., `#(= :bad-arg (:type (ex-data %)))`).
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 e f)(catch-cpu t e1 f1 e2 f2)(catch-cpu t e1 f1 e2 f2 e3 f3)(catch-cpu t e1 f1 e2 f2 e3 f3 e4 f4)(catch-cpu t e1 f1 e2 f2 e3 f3 e4 f4 e5 f5)(catch-cpu t e1 f1 e2 f2 e3 f3 e4 f4 e5 f5 & pairs)Handle errors from a task with a recovery function.
Executes on the platform thread pool. CLJ only.
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. CLJ only.
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-abort signal)If abort signal has been triggered, throw.
If abort signal has been triggered, throw.
(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 body on the platform thread pool and return a task.
Use for CPU-bound work that shouldn't run on virtual threads. Returns immediately; body runs in the background. Nested tasks in the result are grounded.
Execute body on the platform thread pool and return a task. Use for CPU-bound work that shouldn't run on virtual threads. Returns immediately; body runs in the background. Nested tasks in the result are grounded.
(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"))))
```(gate n)Create a gate that limits concurrent task execution to n at a time.
A gate acts like a semaphore: tasks created with gate-task wait for
an available permit before running. When a task completes (success, error,
or cancellation), its permit is released for the next waiting task.
Gates participate in structured concurrency: cancelling a gate cancels all tasks created through it.
Create a gate that limits concurrent task execution to `n` at a time. A gate acts like a semaphore: tasks created with [[gate-task]] wait for an available permit before running. When a task completes (success, error, or cancellation), its permit is released for the next waiting task. Gates participate in structured concurrency: cancelling a gate cancels all tasks created through it.
(gate-task gate & body)Create a task that runs through gate, waiting for an available permit.
The task executes body when a permit is acquired.
Returns immediately with a task that will eventually contain the result of body.
Example:
(let [g (gate 2)] ; max 2 concurrent
(qfor [url urls]
(gate-task g
(fetch url))))
Create a task that runs through `gate`, waiting for an available permit.
The task executes `body` when a permit is acquired.
Returns immediately with a task that will eventually contain the result of `body`.
Example:
```clojure
(let [g (gate 2)] ; max 2 concurrent
(qfor [url urls]
(gate-task g
(fetch url))))
```(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)))
```(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.
Args:
t Task to monitorms-or-dur Duration after which to trigger side effect:
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 propagate and fail the task.Returns the original task unchanged - result and timing are unaffected.
Example:
(-> (slow-operation)
(monitor 5000
#(log/warn "Operation exceeded 5s")))
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.
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 propagate and fail the task.
Returns the original task unchanged - result and timing are unaffected.
Example:
```clojure
(-> (slow-operation)
(monitor 5000
#(log/warn "Operation exceeded 5s")))
```(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 via deliver, fail, or cancel. In both Clojure and ClojureScript,
the promise result can be set by executing the promise as a function of a value.
Chaining operations work as with tasks. Promises can be cancelled, which propagates cancellation to chained tasks.
If a Task is set as the promise value, the task is resolved, and the value that it contains is set as the value of the promise.
(def p (q/promise))
(deliver p :result) ; Delivers :result to the promise
(p :result) ; also works
(cancel p) ; cancels the promise if not yet settled
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 via `deliver`, `fail`, or `cancel`. In both Clojure and ClojureScript,
the promise result can be set by executing the promise as a function of a value.
Chaining operations work as with tasks. Promises can be cancelled, which
propagates cancellation to chained tasks.
If a Task is set as the promise value, the task is resolved, and the value that
it contains is set as the value of the promise.
```clojure
(def p (q/promise))
(deliver p :result) ; Delivers :result to the promise
(p :result) ; also works
(cancel p) ; cancels the promise if not yet settled
```
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 body synchronously and return a task containing the result.
Runs on the calling thread. If the result contains nested tasks, they are awaited in parallel and their values inlined (grounding).
Execute body synchronously and return a task containing the result. Runs on the calling thread. If the result contains nested tasks, they are awaited in parallel and their values inlined (grounding).
(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).
Only called for non-nil results.tasks Tasks to raceExample:
(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).
Only called for non-nil results.
- `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:
java.time.Duration: non-negative durationdefault Optional value to return after sleep (default: nil)
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 100 #(rand-int 10)) ; Sleep 100ms, then call function
@(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 100 #(rand-int 10)) ; Sleep 100ms, then call function
@(sleep (Duration/ofSeconds 1)) ; Sleep 1 second, return nil
@(sleep 100 (Exception. "Boom")) ; Sleep 100ms, then throw
```(task & body)Execute body asynchronously on a virtual thread and return a task.
Returns immediately; body runs in the background. Deref blocks until the task settles. Nested tasks in the result are grounded.
Execute body asynchronously on a virtual thread and return a task. Returns immediately; body runs in the background. Deref blocks until the task settles. Nested tasks in the result are grounded.
(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 is an async primitive that Quiescent handles specially.
True for: Task, Promise, CompletableFuture (CLJ), Future (CLJ), js/Promise (CLJS). False for: nil, plain values, collections.
Note: core.async channels become taskable when the optional
co.multiply.quiescent.adapter.core-async namespace is required.
Returns true if v is an async primitive that Quiescent handles specially. True for: Task, Promise, CompletableFuture (CLJ), Future (CLJ), js/Promise (CLJS). False for: nil, plain values, collections. Note: core.async channels become taskable when the optional `co.multiply.quiescent.adapter.core-async` namespace is required.
(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 the equivalent of (f @task)
Multiple tasks:
(then t1 t2 t3 f) - Waits for all tasks in parallel, calls the equivalent of (f @t1 @t2 @t3)
Chain a function after one or more tasks complete. Single task: `(then task f)` - Calls the equivalent of `(f @task)` Multiple tasks: `(then t1 t2 t3 f)` - Waits for all tasks in parallel, calls the equivalent of `(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 the equivalent of (f @task)
Multiple tasks:
(then t1 t2 t3 f) - Waits for all tasks in parallel, calls the equivalent of (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 the equivalent of `(f @task)` Multiple tasks: `(then t1 t2 t3 f)` - Waits for all tasks in parallel, calls the equivalent of `(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.
(time t side-effect-fn)(time t start side-effect-fn)Measures the duration of a task and reports it via a side-effect function.
The side-effect function receives four arguments: the task's value (or nil), exception (or nil), cancelled flag, and elapsed milliseconds as a number.
By default, timing starts when time is called. To include task construction
time in the measurement, capture the current time beforehand and pass it as
start-ms.
Args:
t Task to measurestart-ms Optional starting time in ms (default: current time at call)side-effect-fn Function called with [value exception cancelled ms]Returns the task unchanged.
Example:
;; Basic usage - timing starts when `time` is attached
(-> (fetch-user id)
(time (fn [v e c ms]
(log/info "fetch-user took" ms "ms"))))
;; Include construction time
(let [start (System/currentTimeMillis)] ; or js/performance.now in CLJS
(-> (fetch-user id)
(time start
(fn [v e c ms]
(log/info "Total time:" ms "ms")))))
Measures the duration of a task and reports it via a side-effect function.
The side-effect function receives four arguments: the task's value (or nil),
exception (or nil), cancelled flag, and elapsed milliseconds as a number.
By default, timing starts when `time` is called. To include task construction
time in the measurement, capture the current time beforehand and pass it as
`start-ms`.
Args:
- `t` Task to measure
- `start-ms` Optional starting time in ms (default: current time at call)
- `side-effect-fn` Function called with `[value exception cancelled ms]`
Returns the task unchanged.
Example:
```clojure
;; Basic usage - timing starts when `time` is attached
(-> (fetch-user id)
(time (fn [v e c ms]
(log/info "fetch-user took" ms "ms"))))
;; Include construction time
(let [start (System/currentTimeMillis)] ; or js/performance.now in CLJS
(-> (fetch-user id)
(time start
(fn [v e c ms]
(log/info "Total time:" ms "ms")))))
```(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 it's asynchronous
and doesn't block the calling thread unless dereferenced.
Args:
t Task to race against timeoutms-or-dur Timeout duration as either:
java.time.Duration: duration objectdefault Optional value/behavior on timeout:
TimeoutExceptionReturns 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 1000 #(rand-int 10)) ; Call fallback fn on timeout
@(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 it's asynchronous
and doesn't block the calling thread unless dereferenced.
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 1000 #(rand-int 10)) ; Call fallback fn on timeout
@(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 |