Package groovy.concurrent

# Interface Awaitable<T>

Type Parameters:
  `T` - the result type

All Known Implementing Classes:
  `DataflowVariable`, `GroovyPromise`

public interface Awaitable<T>
Core abstraction for asynchronous computations in Groovy.

`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 order
- `Awaitable.any(a, b)` — returns the result of the first task to complete
- `Awaitable.first(a, b, c)` — returns the result of the first task to complete successfully (like JavaScript's `Promise.any()`; only fails when all sources fail)
- `Awaitable.allSettled(a, b)` — waits for all tasks to settle (succeed or fail), returning an `AwaitResult` list
- `Awaitable.delay(ms)` — completes after a non-blocking delay
- `Awaitable.orTimeoutMillis(task, ms)` — fails with `TimeoutException` if the task does not complete in time
- `Awaitable.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 an `Awaitable`
- `Awaitable.of(value)` — wraps an already-available value in a completed `Awaitable`
- `Awaitable.failed(error)` — wraps an exception in an immediately-failed `Awaitable`
- `Awaitable.go { ... }` — lightweight task spawn (Groovy extension method)

Instance continuations provide ergonomic composition without exposing raw `CompletableFuture` APIs:

- `then(Function)` and `thenCompose(Function)` for success chaining
- `thenAccept(Consumer)` for side-effecting continuations
- `exceptionally(Function)`, `whenComplete(BiConsumer)`, and `handle(BiFunction)` for failure/completion handling
- `orTimeout(long, TimeUnit)` and `completeOnTimeout(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:
  - `GroovyPromise`
  - `AwaitableAdapter`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static Awaitable<List<Object>>`
  `all(Object... sources)`
  Returns an `Awaitable` that completes when all given sources complete successfully, with a list of their results in order.
  `static Awaitable<List<AwaitResult<Object>>>`
  `allSettled(Object... sources)`
  Returns an `Awaitable` that completes when all given sources have settled (succeeded or failed), with a list of `AwaitResult` objects describing each outcome.
  `static <T> Awaitable<T>`
  `any(Object... sources)`
  Returns an `Awaitable` that completes when the first of the given sources completes, with the winner's result.
  `boolean`
  `cancel()`
  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.
  `default Awaitable<T>`
  `completeOnTimeout(T fallback, long duration, TimeUnit unit)`
  Returns a new `Awaitable` that 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.
  `default Awaitable<T>`
  `completeOnTimeoutMillis(T fallback, long timeoutMillis)`
  Returns a new `Awaitable` that completes with the supplied fallback value if this computation does not finish before the timeout expires.
  `static Awaitable<Void>`
  `delay(long milliseconds)`
  Returns an `Awaitable` that completes after the specified delay.
  `static Awaitable<Void>`
  `delay(long duration, TimeUnit unit)`
  Returns an `Awaitable` that completes after the specified delay.
  `Awaitable<T>`
  `exceptionally(Function<Throwable,? extends T> fn)`
  Returns a new `Awaitable` that, if this one completes exceptionally, applies the given function to the exception to produce a recovery value.
  `static <T> Awaitable<T>`
  `failed(Throwable error)`
  Returns an already-failed `Awaitable` with the given exception.
  `static <T> Awaitable<T>`
  `first(Object... sources)`
  Returns an `Awaitable` that completes with the result of the first source that succeeds.
  `static <T> Awaitable<T>`
  `from(Object source)`
  Converts the given source to an `Awaitable`.
  `T`
  `get()`
  Blocks until the computation completes and returns the result.
  `T`
  `get(long timeout, TimeUnit unit)`
  Blocks until the computation completes or the timeout expires.
  `static Executor`
  `getExecutor()`
  Returns the current executor used for `async` operations.
  `static <T> Awaitable<T>`
  `go(Supplier<T> supplier)`
  Spawns a lightweight task on the default executor.
  `default <U> Awaitable<U>`
  `handle(BiFunction<? super T,Throwable,? extends U> fn)`
  Returns a new `Awaitable` that handles both the successful and the exceptional completion paths in a single continuation.
  `boolean`
  `isCancelled()`
  Returns `true` if the computation was cancelled before completing normally.
  `boolean`
  `isCompletedExceptionally()`
  Returns `true` if this computation completed exceptionally (including cancellation).
  `boolean`
  `isDone()`
  Returns `true` if the computation has completed (normally, exceptionally, or via cancellation).
  `static boolean`
  `isVirtualThreadsAvailable()`
  Returns `true` if the running JVM supports virtual threads (JDK 21+).
  `static <T> Awaitable<T>`
  `of(T value)`
  Returns an already-completed `Awaitable` with the given value.
  `default Awaitable<T>`
  `orTimeout(long duration, TimeUnit unit)`
  Returns a new `Awaitable` that fails with `TimeoutException` if this computation does not complete within the specified duration.
  `static <T> Awaitable<T>`
  `orTimeout(Object source, long duration, TimeUnit unit)`
  Adapts the given source and applies a non-blocking fail-fast timeout with explicit `TimeUnit`.
  `default Awaitable<T>`
  `orTimeoutMillis(long timeoutMillis)`
  Returns a new `Awaitable` that fails with `TimeoutException` if this computation does not complete within the specified milliseconds.
  `static <T> Awaitable<T>`
  `orTimeoutMillis(Object source, long timeoutMillis)`
  Adapts the given source to an `Awaitable` and applies a non-blocking fail-fast timeout.
  `static void`
  `setExecutor(Executor executor)`
  Sets the executor to use for `async` operations.
  `<U> Awaitable<U>`
  `then(Function<? super T,? extends U> fn)`
  Returns a new `Awaitable` whose result is obtained by applying the given function to this awaitable's result when it completes.
  `default Awaitable<Void>`
  `thenAccept(Consumer<? super T> action)`
  Returns a new `Awaitable` that, when this one completes normally, invokes the given consumer and completes with `null`.
  `<U> Awaitable<U>`
  `thenCompose(Function<? super T,? extends Awaitable<U>> fn)`
  Returns a new `Awaitable` produced by applying the given async function to this awaitable's result, flattening the nested `Awaitable`.
  `CompletableFuture<T>`
  `toCompletableFuture()`
  Converts this `Awaitable` to a JDK `CompletableFuture` for interoperability with APIs that require it.
  `default Awaitable<T>`
  `whenComplete(BiConsumer<? super T,? super Throwable> action)`
  Returns a new `Awaitable` that invokes the given action when this computation completes, regardless of success or failure.
  `static <T> T`
  `withScope(Function<AsyncScope,T> body)`
  Convenience delegate to `AsyncScope.withScope(Function)`.

- ## Method Details

  - ### get

    T get() throws InterruptedException, ExecutionException
    Blocks until the computation completes and returns the result.
    Returns:
      the computed result
    Throws:
      `InterruptedException` - if the calling thread is interrupted while waiting
      `ExecutionException` - 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 wait
      `unit` - the time unit of the timeout argument
    Returns:
      the computed result
    Throws:
      `InterruptedException` - if the calling thread is interrupted while waiting
      `ExecutionException` - if the computation completed exceptionally
      `TimeoutException` - if the wait timed out

  - ### isDone

    boolean isDone()
    Returns `true` if the computation has completed (normally, exceptionally, or via cancellation).
    Returns:
      `true` if 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 a `CancellationException`.
    Returns:
      `true` if the computation was successfully cancelled

  - ### isCancelled

    boolean isCancelled()
    Returns `true` if the computation was cancelled before completing normally.
    Returns:
      `true` if cancelled

  - ### isCompletedExceptionally

    boolean isCompletedExceptionally()
    Returns `true` if this computation completed exceptionally (including cancellation).
    Returns:
      `true` if completed with an error or cancellation

  - ### then

    <U> Awaitable<U> then(Function<? super T,? extends U> fn)
    Returns a new `Awaitable` whose 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

    <U> Awaitable<U> thenCompose(Function<? super T,? extends Awaitable<U>> fn)
    Returns a new `Awaitable` produced by applying the given async function to this awaitable's result, flattening the nested `Awaitable`. This is the monadic `flatMap` operation for awaitables.
    Type Parameters:
      `U` - the type of the inner awaitable's result
    Parameters:
      `fn` - the async mapping function that returns an `Awaitable`
    Returns:
      a new awaitable holding the inner result

  - ### thenAccept

    default Awaitable<Void> thenAccept(Consumer<? super T> action)
    Returns a new `Awaitable` that, when this one completes normally, invokes the given consumer and completes with `null`.

    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

    Awaitable<T> exceptionally(Function<Throwable,? extends T> fn)
    Returns a new `Awaitable` that, 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

    default Awaitable<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
    Returns a new `Awaitable` that 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` / `CompletionException` wrappers.

    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

    default <U> Awaitable<U> handle(BiFunction<? super T,Throwable,? extends U> fn)
    Returns a new `Awaitable` that 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

    default Awaitable<T> orTimeoutMillis(long timeoutMillis)
    Returns a new `Awaitable` that fails with `TimeoutException` if this computation does not complete within the specified milliseconds.

    Unlike `get(long, TimeUnit)`, this is a non-blocking, composable timeout combinator: it returns another `Awaitable` that can itself be awaited, chained, or passed to `all(Object...)` / `any(Object...)`.

    Parameters:
      `timeoutMillis` - the timeout duration in milliseconds
    Returns:
      a new awaitable with timeout semantics
    Since:
      6.0.0

  - ### orTimeout

    default Awaitable<T> orTimeout(long duration, TimeUnit unit)
    Returns a new `Awaitable` that fails with `TimeoutException` if this computation does not complete within the specified duration.
    Parameters:
      `duration` - the timeout duration
      `unit` - the time unit
    Returns:
      a new awaitable with timeout semantics
    Since:
      6.0.0

  - ### completeOnTimeoutMillis

    default Awaitable<T> completeOnTimeoutMillis(T fallback, long timeoutMillis)
    Returns a new `Awaitable` that 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 expires
      `timeoutMillis` - the timeout duration in milliseconds
    Returns:
      a new awaitable that yields either the original result or the fallback
    Since:
      6.0.0

  - ### completeOnTimeout

    default Awaitable<T> completeOnTimeout(T fallback, long duration, TimeUnit unit)
    Returns a new `Awaitable` that 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 expires
      `duration` - the timeout duration
      `unit` - 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 this `Awaitable` to a JDK `CompletableFuture` for interoperability with APIs that require it.
    Returns:
      a `CompletableFuture` representing this computation

  - ### from

    static <T> Awaitable<T> from(Object source)
    Converts the given source to an `Awaitable`.

    If the source is already an `Awaitable`, it is returned as-is. Otherwise, the `AwaitableAdapterRegistry` is consulted to find a suitable adapter. Built-in adapters handle `CompletableFuture`, `CompletionStage`, and `Future`; 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; if `null`, returns a completed awaitable with a `null` result
    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

    static <T> Awaitable<T> of(T value)
    Returns an already-completed `Awaitable` with the given value. Useful for returning a pre-computed result from an API that requires an `Awaitable` return type.
    Type Parameters:
      `T` - the result type
    Parameters:
      `value` - the result value (may be `null`)
    Returns:
      a completed awaitable

  - ### failed

    static <T> Awaitable<T> failed(Throwable error)
    Returns an already-failed `Awaitable` with 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 be `null`
    Returns:
      an immediately-failed awaitable
    Throws:
      `NullPointerException` - if `error` is `null`

  - ### go

    static <T> Awaitable<T> go(Supplier<T> supplier)
    Spawns a lightweight task on the default executor.
    Type Parameters:
      `T` - the result type
    Parameters:
      `supplier` - the task body
    Returns:
      an awaitable representing the spawned task
    Since:
      6.0.0

  - ### withScope

    static <T> T withScope(Function<AsyncScope,T> body)
    Convenience delegate to `AsyncScope.withScope(Function)`. Creates a structured concurrency scope, executes the body within it, and ensures all child tasks complete before returning.
    Type Parameters:
      `T` - the result type
    Parameters:
      `body` - the function receiving the scope
    Returns:
      the body's return value
    Since:
      6.0.0
    See Also:
      - `AsyncScope.withScope(Function)`

  - ### all

    static Awaitable<List<Object>> all(Object... sources)
    Returns an `Awaitable` that 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 `await` the 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

    static <T> Awaitable<T> any(Object... sources)
    Returns an `Awaitable` that 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

    static <T> Awaitable<T> first(Object... sources)
    Returns an `Awaitable` that 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 an `IllegalStateException` whose suppressed array contains every individual error.

    This is the Groovy equivalent of JavaScript's `Promise.any()`. Contrast with `any(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 be `null`, empty, or contain `null` elements
    Returns:
      an awaitable that resolves with the first successful result
    Throws:
      `IllegalArgumentException` - if `sources` is `null`, empty, or contains `null` elements
    Since:
      6.0.0
    See Also:
      - `AsyncSupport.firstAsync — implementation`

  - ### allSettled

    static Awaitable<List<AwaitResult<Object>>> allSettled(Object... sources)
    Returns an `Awaitable` that completes when all given sources have settled (succeeded or failed), with a list of `AwaitResult` objects 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

    static Awaitable<Void> delay(long milliseconds)
    Returns an `Awaitable` that 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

    static Awaitable<Void> delay(long duration, TimeUnit unit)
    Returns an `Awaitable` that 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

    static <T> Awaitable<T> orTimeoutMillis(Object source, long timeoutMillis)
    Adapts the given source to an `Awaitable` and applies a non-blocking fail-fast timeout. Returns a new awaitable that fails with `TimeoutException` if the source does not complete before the deadline elapses.

    The source may be a Groovy `Awaitable`, a JDK `CompletableFuture`/`CompletionStage`, or any type supported by `AwaitableAdapterRegistry`. 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 out
      `timeoutMillis` - the timeout duration in milliseconds
    Returns:
      a new awaitable with timeout semantics
    Since:
      6.0.0

  - ### orTimeout

    static <T> Awaitable<T> orTimeout(Object source, long duration, TimeUnit unit)
    Adapts the given source and applies a non-blocking fail-fast timeout with explicit `TimeUnit`.
    Type Parameters:
      `T` - the result type
    Parameters:
      `source` - the async source to time out
      `duration` - the timeout duration
      `unit` - the time unit
    Returns:
      a new awaitable with timeout semantics
    Since:
      6.0.0

  - ### completeOnTimeoutMillis

    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.
    Type Parameters:
      `T` - the result type
    Parameters:
      `source` - the async source to wait for
      `fallback` - the fallback value to use on timeout
      `timeoutMillis` - the timeout duration in milliseconds
    Returns:
      a new awaitable yielding either the original result or the fallback
    Since:
      6.0.0

  - ### completeOnTimeout

    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.
    Type Parameters:
      `T` - the result type
    Parameters:
      `source` - the async source to wait for
      `fallback` - the fallback value to use on timeout
      `duration` - the timeout duration
      `unit` - the time unit
    Returns:
      a new awaitable yielding either the original result or the fallback
    Since:
      6.0.0

  - ### getExecutor

    static Executor getExecutor()
    Returns the current executor used for `async` operations.

    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.parallelism` system property, default `256`).

    This method is thread-safe and may be called from any thread.

    Returns:
      the current executor, never `null`
    See Also:
      - `setExecutor(Executor)`

  - ### setExecutor

    static void setExecutor(Executor executor)
    Sets the executor to use for `async` operations.

    Pass `null` to reset to the default executor. The change takes effect immediately for all subsequent `async` method 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, or `null` to restore the default executor
    See Also:
      - `getExecutor()`

  - ### isVirtualThreadsAvailable

    static boolean isVirtualThreadsAvailable()
    Returns `true` if 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:
      `true` if virtual threads are available