Interface Awaitable<T>
- Type Parameters:
T- the result type
- All Known Implementing Classes:
GroovyPromise
Awaitable represents a computation that may not have completed
yet. It serves as both an instance type (the result of async
methods and the input to await) and a static API surface for
combinators, factories, and configuration.
Static combinators (all return Awaitable, suitable for use
with await). Multi-argument await desugars to
Awaitable.all(...), so await(a, b) and await a, b
are shorthand for await Awaitable.all(a, b):
Awaitable.all(a, b, c)— waits for all tasks to complete, returning their results in orderAwaitable.any(a, b)— returns the result of the first task to completeAwaitable.first(a, b, c)— returns the result of the first task to complete successfully (like JavaScript'sPromise.any(); only fails when all sources fail)Awaitable.allSettled(a, b)— waits for all tasks to settle (succeed or fail), returning anAwaitResultlistAwaitable.delay(ms)— completes after a non-blocking delayAwaitable.orTimeoutMillis(task, ms)— fails withTimeoutExceptionif the task does not complete in timeAwaitable.completeOnTimeoutMillis(task, fallback, ms)— uses a fallback value when the timeout expires
Static factories and conversion:
Awaitable.from(source)— converts any supported async type (CompletableFuture, CompletionStage, Future, etc.) to anAwaitableAwaitable.of(value)— wraps an already-available value in a completedAwaitableAwaitable.failed(error)— wraps an exception in an immediately-failedAwaitableAwaitable.go { ... }— lightweight task spawn
Instance continuations provide ergonomic composition without exposing
raw CompletableFuture APIs:
then(Function)andthenCompose(Function)for success chainingthenAccept(Consumer)for side-effecting continuationsexceptionally(Function),whenComplete(BiConsumer), andhandle(BiFunction)for failure/completion handlingorTimeout(long, TimeUnit)andcompleteOnTimeout(Object, long, TimeUnit)for deadline composition
Third-party frameworks (RxJava, Reactor, etc.) can integrate by registering
an AwaitableAdapter via AwaitableAdapterRegistry.
The default implementation, GroovyPromise, delegates to
CompletableFuture but users never need to depend on that detail.
- Since:
- 6.0.0
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionReturns anAwaitablethat completes when all given sources complete successfully, with a list of their results in order.static Awaitable<List<AwaitResult<Object>>>allSettled(Object... sources) Returns anAwaitablethat completes when all given sources have settled (succeeded or failed), with a list ofAwaitResultobjects describing each outcome.static <T> Awaitable<T>Returns anAwaitablethat completes when the first of the given sources completes, with the winner's result.booleancancel()Attempts to cancel the computation.static <T> Awaitable<T>completeOnTimeout(Object source, T fallback, long duration, TimeUnit unit) Adapts the given source and returns a new awaitable that yields the supplied fallback value if the timeout expires first.completeOnTimeout(T fallback, long duration, TimeUnit unit) Returns a newAwaitablethat completes with the supplied fallback value if this computation does not finish before the timeout expires.static <T> Awaitable<T>completeOnTimeoutMillis(Object source, T fallback, long timeoutMillis) Adapts the given source and returns a new awaitable that yields the supplied fallback value if the timeout expires first.completeOnTimeoutMillis(T fallback, long timeoutMillis) Returns a newAwaitablethat completes with the supplied fallback value if this computation does not finish before the timeout expires.delay(long milliseconds) Returns anAwaitablethat completes after the specified delay.Returns anAwaitablethat completes after the specified delay.exceptionally(Function<Throwable, ? extends T> fn) Returns a newAwaitablethat, if this one completes exceptionally, applies the given function to the exception to produce a recovery value.static <T> Awaitable<T>Returns an already-failedAwaitablewith the given exception.static <T> Awaitable<T>Returns anAwaitablethat completes with the result of the first source that succeeds.static <T> Awaitable<T>Converts the given source to anAwaitable.get()Blocks until the computation completes and returns the result.Blocks until the computation completes or the timeout expires.static ExecutorReturns the current executor used forasyncoperations.static <T> Awaitable<T>Spawns a lightweight task.default <U> Awaitable<U>handle(BiFunction<? super T, Throwable, ? extends U> fn) Returns a newAwaitablethat handles both the successful and the exceptional completion paths in a single continuation.booleanReturnstrueif the computation was cancelled before completing normally.booleanReturnstrueif this computation completed exceptionally (including cancellation).booleanisDone()Returnstrueif the computation has completed (normally, exceptionally, or via cancellation).static booleanReturnstrueif the running JVM supports virtual threads (JDK 21+).static <T> Awaitable<T>of(T value) Returns an already-completedAwaitablewith the given value.Returns a newAwaitablethat fails withTimeoutExceptionif this computation does not complete within the specified duration.static <T> Awaitable<T>Adapts the given source and applies a non-blocking fail-fast timeout with explicitTimeUnit.orTimeoutMillis(long timeoutMillis) Returns a newAwaitablethat fails withTimeoutExceptionif this computation does not complete within the specified milliseconds.static <T> Awaitable<T>orTimeoutMillis(Object source, long timeoutMillis) Adapts the given source to anAwaitableand applies a non-blocking fail-fast timeout.static voidsetExecutor(Executor executor) Sets the executor to use forasyncoperations.<U> Awaitable<U>Returns a newAwaitablewhose result is obtained by applying the given function to this awaitable's result when it completes.thenAccept(Consumer<? super T> action) Returns a newAwaitablethat, when this one completes normally, invokes the given consumer and completes withnull.<U> Awaitable<U>thenCompose(Function<? super T, ? extends Awaitable<U>> fn) Returns a newAwaitableproduced by applying the given async function to this awaitable's result, flattening the nestedAwaitable.Converts thisAwaitableto a JDKCompletableFuturefor interoperability with APIs that require it.whenComplete(BiConsumer<? super T, ? super Throwable> action) Returns a newAwaitablethat invokes the given action when this computation completes, regardless of success or failure.static <T> TConvenience delegate toAsyncScope.withScope(Closure).
-
Method Details
-
get
Blocks until the computation completes and returns the result.- Returns:
- the computed result
- Throws:
InterruptedException- if the calling thread is interrupted while waitingExecutionException- if the computation completed exceptionally
-
get
T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException Blocks until the computation completes or the timeout expires.- Parameters:
timeout- the maximum time to waitunit- the time unit of the timeout argument- Returns:
- the computed result
- Throws:
InterruptedException- if the calling thread is interrupted while waitingExecutionException- if the computation completed exceptionallyTimeoutException- if the wait timed out
-
isDone
boolean isDone()Returnstrueif the computation has completed (normally, exceptionally, or via cancellation).- Returns:
trueif complete
-
cancel
boolean cancel()Attempts to cancel the computation. If the computation has not yet started or is still running, it will be cancelled with aCancellationException.- Returns:
trueif the computation was successfully cancelled
-
isCancelled
boolean isCancelled()Returnstrueif the computation was cancelled before completing normally.- Returns:
trueif cancelled
-
isCompletedExceptionally
boolean isCompletedExceptionally()Returnstrueif this computation completed exceptionally (including cancellation).- Returns:
trueif completed with an error or cancellation
-
then
Returns a newAwaitablewhose result is obtained by applying the given function to this awaitable's result when it completes.- Type Parameters:
U- the type of the mapped result- Parameters:
fn- the mapping function- Returns:
- a new awaitable holding the mapped result
-
thenCompose
Returns a newAwaitableproduced by applying the given async function to this awaitable's result, flattening the nestedAwaitable. This is the monadicflatMapoperation for awaitables.- Type Parameters:
U- the type of the inner awaitable's result- Parameters:
fn- the async mapping function that returns anAwaitable- Returns:
- a new awaitable holding the inner result
-
thenAccept
Returns a newAwaitablethat, when this one completes normally, invokes the given consumer and completes withnull.Useful at API boundaries where you need to attach logging, metrics, or other side effects without blocking for the result.
- Parameters:
action- the side-effecting consumer to invoke on success- Returns:
- a new awaitable that completes after the action runs
- Since:
- 6.0.0
-
exceptionally
Returns a newAwaitablethat, if this one completes exceptionally, applies the given function to the exception to produce a recovery value. The throwable passed to the function is deeply unwrapped to strip JDK wrapper layers.- Parameters:
fn- the recovery function- Returns:
- a new awaitable that recovers from failures
-
whenComplete
Returns a newAwaitablethat invokes the given action when this computation completes, regardless of success or failure.The supplied throwable is transparently unwrapped so handlers see the original failure rather than
ExecutionException/CompletionExceptionwrappers.- Parameters:
action- the completion callback receiving the result or failure- Returns:
- a new awaitable that completes with the original result
- Since:
- 6.0.0
-
handle
Returns a newAwaitablethat handles both the successful and the exceptional completion paths in a single continuation.The supplied throwable is transparently unwrapped so the handler sees the original failure. This provides a single place for success/failure projection, combining both paths in one callback.
- Type Parameters:
U- the projected result type- Parameters:
fn- the handler receiving either the value or the failure- Returns:
- a new awaitable holding the handler's result
- Since:
- 6.0.0
-
orTimeoutMillis
Returns a newAwaitablethat fails withTimeoutExceptionif this computation does not complete within the specified milliseconds.Unlike
get(long, TimeUnit), this is a non-blocking, composable timeout combinator: it returns anotherAwaitablethat can itself be awaited, chained, or passed toall(Object...)/any(Object...).- Parameters:
timeoutMillis- the timeout duration in milliseconds- Returns:
- a new awaitable with timeout semantics
- Since:
- 6.0.0
-
orTimeout
Returns a newAwaitablethat fails withTimeoutExceptionif this computation does not complete within the specified duration.- Parameters:
duration- the timeout durationunit- the time unit- Returns:
- a new awaitable with timeout semantics
- Since:
- 6.0.0
-
completeOnTimeoutMillis
Returns a newAwaitablethat completes with the supplied fallback value if this computation does not finish before the timeout expires.- Parameters:
fallback- the value to use when the timeout expirestimeoutMillis- the timeout duration in milliseconds- Returns:
- a new awaitable that yields either the original result or the fallback
- Since:
- 6.0.0
-
completeOnTimeout
Returns a newAwaitablethat completes with the supplied fallback value if this computation does not finish before the timeout expires.- Parameters:
fallback- the value to use when the timeout expiresduration- the timeout durationunit- the time unit- Returns:
- a new awaitable that yields either the original result or the fallback
- Since:
- 6.0.0
-
toCompletableFuture
CompletableFuture<T> toCompletableFuture()Converts thisAwaitableto a JDKCompletableFuturefor interoperability with APIs that require it.- Returns:
- a
CompletableFuturerepresenting this computation
-
from
Converts the given source to anAwaitable.If the source is already an
Awaitable, it is returned as-is. Otherwise, theAwaitableAdapterRegistryis consulted to find a suitable adapter. Built-in adapters handleCompletableFuture,CompletionStage, andFuture; third-party frameworks can register additional adapters via the registry.This is the recommended entry point for converting external async types to
Awaitable:Awaitable<String> aw = Awaitable.from(someCompletableFuture) Awaitable<Integer> aw2 = Awaitable.from(someReactorMono)
- Type Parameters:
T- the result type- Parameters:
source- the source object; ifnull, returns a completed awaitable with anullresult- Returns:
- an awaitable backed by the source
- Throws:
IllegalArgumentException- if no adapter supports the source type- Since:
- 6.0.0
- See Also:
-
AwaitableAdapterRegistry.toAwaitable(Object)
-
of
Returns an already-completedAwaitablewith the given value. Useful for returning a pre-computed result from an API that requires anAwaitablereturn type.- Type Parameters:
T- the result type- Parameters:
value- the result value (may benull)- Returns:
- a completed awaitable
-
failed
Returns an already-failedAwaitablewith the given exception.Useful for signaling a synchronous error from an API that returns
Awaitable, without spawning a thread:if (id < 0) return Awaitable.failed(new IllegalArgumentException("negative id"))- Type Parameters:
T- the nominal result type (never produced, since the awaitable is failed)- Parameters:
error- the exception to wrap; must not benull- Returns:
- an immediately-failed awaitable
- Throws:
NullPointerException- iferrorisnull
-
go
Spawns a lightweight task.- Type Parameters:
T- the result type- Parameters:
closure- the task body- Returns:
- an awaitable representing the spawned task
- Since:
- 6.0.0
-
withScope
Convenience delegate toAsyncScope.withScope(Closure). Creates a structured concurrency scope, executes the closure within it, and ensures all child tasks complete before returning.- Type Parameters:
T- the result type- Parameters:
body- the closure receiving the scope- Returns:
- the closure's return value
- Since:
- 6.0.0
- See Also:
-
all
Returns anAwaitablethat completes when all given sources complete successfully, with a list of their results in order.Like JavaScript's
Promise.all(), the combined awaitable fails as soon as the first source fails. Remaining sources are not cancelled automatically; cancel them explicitly if that is required by your workflow.Unlike blocking APIs, this returns immediately and the caller should
awaitthe result. All three forms below are equivalent:// Explicit all() call def results = await Awaitable.all(task1, task2, task3) // Parenthesized multi-arg await — syntactic sugar def results = await(task1, task2, task3) // Unparenthesized multi-arg await — most concise form def results = await task1, task2, task3
- Parameters:
sources- the awaitables, futures, or adapted objects to wait for- Returns:
- an awaitable that resolves to a list of results
-
any
Returns anAwaitablethat completes when the first of the given sources completes, with the winner's result.def winner = await Awaitable.any(task1, task2)
- Parameters:
sources- the awaitables to race- Returns:
- an awaitable that resolves to the first completed result
-
first
Returns anAwaitablethat completes with the result of the first source that succeeds. Individual failures are silently absorbed; only when all sources have failed does the returned awaitable reject with anIllegalStateExceptionwhose suppressed array contains every individual error.This is the Groovy equivalent of JavaScript's
Promise.any(). Contrast withany(Object...)which returns the first result to complete regardless of success or failure.Typical use cases:
- Hedged requests — send the same request to multiple endpoints, use whichever responds first successfully
- Graceful degradation — try a primary data source, then a fallback, then a cache, accepting the first success
- Redundant queries — send the same query to different database replicas for improved latency
// Hedged HTTP request def response = await Awaitable.first( fetchFromPrimary(), fetchFromFallback(), fetchFromCache() )- Type Parameters:
T- the result type- Parameters:
sources- the awaitables to race for first success; must not benull, empty, or containnullelements- Returns:
- an awaitable that resolves with the first successful result
- Throws:
IllegalArgumentException- ifsourcesisnull, empty, or containsnullelements- Since:
- 6.0.0
- See Also:
-
allSettled
Returns anAwaitablethat completes when all given sources have settled (succeeded or failed), with a list ofAwaitResultobjects describing each outcome.Never throws for individual failures; they are captured in the result list.
def results = await Awaitable.allSettled(task1, task2) results.each { println it.success ? it.value : it.error }- Parameters:
sources- the awaitables to settle- Returns:
- an awaitable that resolves to a list of settled results
-
delay
Returns anAwaitablethat completes after the specified delay. Does not block any thread; the delay is handled by a scheduled executor.await Awaitable.delay(1000) // pause for 1 second
- Parameters:
milliseconds- the delay in milliseconds (must be ≥ 0)- Returns:
- an awaitable that completes after the delay
-
delay
Returns anAwaitablethat completes after the specified delay.- Parameters:
duration- the delay duration (must be ≥ 0)unit- the time unit- Returns:
- an awaitable that completes after the delay
-
orTimeoutMillis
Adapts the given source to anAwaitableand applies a non-blocking fail-fast timeout. Returns a new awaitable that fails withTimeoutExceptionif the source does not complete before the deadline elapses.The source may be a Groovy
Awaitable, a JDKCompletableFuture/CompletionStage, or any type supported byAwaitableAdapterRegistry. This provides a concise timeout combinator that returns another awaitable rather than requiring structural timeout blocks.- Type Parameters:
T- the result type- Parameters:
source- the async source to time outtimeoutMillis- the timeout duration in milliseconds- Returns:
- a new awaitable with timeout semantics
- Since:
- 6.0.0
-
orTimeout
Adapts the given source and applies a non-blocking fail-fast timeout with explicitTimeUnit.- Type Parameters:
T- the result type- Parameters:
source- the async source to time outduration- the timeout durationunit- the time unit- Returns:
- a new awaitable with timeout semantics
- Since:
- 6.0.0
-
completeOnTimeoutMillis
Adapts the given source and returns a new awaitable that yields the supplied fallback value if the timeout expires first.- Type Parameters:
T- the result type- Parameters:
source- the async source to wait forfallback- the fallback value to use on timeouttimeoutMillis- the timeout duration in milliseconds- Returns:
- a new awaitable yielding either the original result or the fallback
- Since:
- 6.0.0
-
completeOnTimeout
Adapts the given source and returns a new awaitable that yields the supplied fallback value if the timeout expires first.- Type Parameters:
T- the result type- Parameters:
source- the async source to wait forfallback- the fallback value to use on timeoutduration- the timeout durationunit- the time unit- Returns:
- a new awaitable yielding either the original result or the fallback
- Since:
- 6.0.0
-
getExecutor
Returns the current executor used forasyncoperations.On JDK 21+, the default is a virtual-thread-per-task executor. On JDK 17–20, a bounded cached daemon thread pool is used (size controlled by the
groovy.async.parallelismsystem property, default256).This method is thread-safe and may be called from any thread.
- Returns:
- the current executor, never
null - See Also:
-
setExecutor
Sets the executor to use forasyncoperations.Pass
nullto reset to the default executor. The change takes effect immediately for all subsequentasyncmethod invocations; tasks already in flight continue using the executor that launched them.This method is thread-safe and may be called from any thread.
- Parameters:
executor- the executor to use, ornullto restore the default executor- See Also:
-
isVirtualThreadsAvailable
static boolean isVirtualThreadsAvailable()Returnstrueif the running JVM supports virtual threads (JDK 21+).When virtual threads are available, the default executor uses a virtual-thread-per-task strategy that scales to millions of concurrent tasks with minimal memory overhead.
- Returns:
trueif virtual threads are available
-