Three-field struct that tracks the lifecycle of an asynchronously-resolved socket assignment.
Every assign written via Musubi.Async.assign_async/3,4 (or seeded by
Musubi.Async.stream_async/3,4) flows through one of the three statuses
below so the client can pattern-match on a discriminated union.
status | result | reason |
|---|---|---|
:loading | prior result (or nil) — stale-while-load | nil |
:ok | the value the user fun produced | nil |
:failed | prior result (or nil) — stale-while-fail | {:error, _} or {:exit, _} |
Wire serialization preserves the three public fields and adds the
runtime-only marker key "__musubi_async__" => true so clients can
distinguish an AsyncResult payload from ordinary maps with similar keys.
Compile-time type marker
Musubi.AsyncResult.of(t) is also the typespec marker used inside state do
field declarations (see Musubi.DSL.State). The runtime struct and the
compile-time marker share a name on purpose: a field declared as
field :profile, AsyncResult.of(UserProfileState.t()) accepts an
%Musubi.AsyncResult{} at runtime.
Examples
iex> Musubi.AsyncResult.loading()
%Musubi.AsyncResult{status: :loading, result: nil, reason: nil}
iex> prior = %Musubi.AsyncResult{status: :ok, result: "snapshot", reason: nil}
iex> Musubi.AsyncResult.loading(prior)
%Musubi.AsyncResult{status: :loading, result: "snapshot", reason: nil}
iex> Musubi.AsyncResult.ok(nil, %{name: "ada"})
%Musubi.AsyncResult{status: :ok, result: %{name: "ada"}, reason: nil}
iex> Musubi.AsyncResult.failed("snapshot", {:error, :timeout})
%Musubi.AsyncResult{status: :failed, result: "snapshot", reason: {:error, :timeout}}
Summary
Types
Failure classification returned by the runtime.
Compile-time field-type marker used inside state do declarations.
Discriminated-union status enum surfaced on the wire.
Functions
Returns a :failed %AsyncResult{} that preserves the prior result for
stale-while-failed UX. reason must be {:error, term} or {:exit, term}.
Returns a fresh %AsyncResult{} in the :loading status with no prior result.
Returns a :loading %AsyncResult{} that preserves the prior result for
stale-while-loading UX.
Types
Failure classification returned by the runtime.
@type of(value) :: %Musubi.AsyncResult{ reason: failure_reason() | nil, result: value | nil, status: status() }
Compile-time field-type marker used inside state do declarations.
@type status() :: :loading | :ok | :failed
Discriminated-union status enum surfaced on the wire.
@type t() :: %Musubi.AsyncResult{ reason: failure_reason() | nil, result: term(), status: status() }
Functions
@spec failed(t() | term(), failure_reason()) :: t()
Returns a :failed %AsyncResult{} that preserves the prior result for
stale-while-failed UX. reason must be {:error, term} or {:exit, term}.
Examples
iex> Musubi.AsyncResult.failed("snapshot", {:error, :unauthorized})
%Musubi.AsyncResult{status: :failed, result: "snapshot", reason: {:error, :unauthorized}}
iex> prior = %Musubi.AsyncResult{status: :ok, result: "snapshot"}
iex> Musubi.AsyncResult.failed(prior, {:exit, :timeout})
%Musubi.AsyncResult{status: :failed, result: "snapshot", reason: {:exit, :timeout}}
@spec loading() :: t()
Returns a fresh %AsyncResult{} in the :loading status with no prior result.
Examples
iex> Musubi.AsyncResult.loading()
%Musubi.AsyncResult{status: :loading, result: nil, reason: nil}
Returns a :loading %AsyncResult{} that preserves the prior result for
stale-while-loading UX.
Accepts either a previously-resolved %AsyncResult{} (its result is
carried forward) or any raw value (used directly as the prior result).
Examples
iex> Musubi.AsyncResult.loading(%Musubi.AsyncResult{status: :ok, result: "snapshot"})
%Musubi.AsyncResult{status: :loading, result: "snapshot", reason: nil}
iex> Musubi.AsyncResult.loading("raw")
%Musubi.AsyncResult{status: :loading, result: "raw", reason: nil}
iex> Musubi.AsyncResult.loading(nil)
%Musubi.AsyncResult{status: :loading, result: nil, reason: nil}
Returns an :ok %AsyncResult{} carrying the produced value. The prior
argument is accepted for symmetry with loading/1/failed/2; on success
the new result always replaces the prior one.
Examples
iex> Musubi.AsyncResult.ok(%Musubi.AsyncResult{status: :loading}, %{name: "ada"})
%Musubi.AsyncResult{status: :ok, result: %{name: "ada"}, reason: nil}
iex> Musubi.AsyncResult.ok(nil, 42)
%Musubi.AsyncResult{status: :ok, result: 42, reason: nil}