# `Musubi.AsyncResult`
[🔗](https://github.com/fahchen/musubi/blob/v0.3.0/lib/musubi/async_result.ex#L1)

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}}

# `failure_reason`

```elixir
@type failure_reason() :: {:error, term()} | {:exit, term()}
```

Failure classification returned by the runtime.

# `of`

```elixir
@type of(value) :: %Musubi.AsyncResult{
  reason: failure_reason() | nil,
  result: value | nil,
  status: status()
}
```

Compile-time field-type marker used inside `state do` declarations.

# `status`

```elixir
@type status() :: :loading | :ok | :failed
```

Discriminated-union status enum surfaced on the wire.

# `t`

```elixir
@type t() :: %Musubi.AsyncResult{
  reason: failure_reason() | nil,
  result: term(),
  status: status()
}
```

# `failed`

```elixir
@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}}

# `loading`

```elixir
@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}

# `loading`

```elixir
@spec loading(t() | term()) :: t()
```

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}

# `ok`

```elixir
@spec ok(t() | term(), term()) :: t()
```

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}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
