Props & data

Async Assigns

assign_async's AsyncResult reaches React as loading, ok and failed

  • assign_async
  • AsyncResult encoder

Key concepts

assign_async/3 in mount/3 starts work in a linked process and immediately returns a Phoenix.LiveView.AsyncResult in its loading state. When the work finishes, LiveView updates the assign for you — either to a successful result or to a failure — with no handle_info/2 to write.

async_live.ex
defmodule MyAppWeb.AsyncLive do
  use MyAppWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, socket |> assign(:mode, :ok) |> load_stats()}
  end

  def render(assigns) do
    ~H"""
    <div class="space-y-4">
      <div class="flex gap-2">
        <button type="button" class="rounded-md border px-3 py-1" phx-click="reload">
          Reload
        </button>
        <button type="button" class="rounded-md border px-3 py-1" phx-click="fail">
          Simulate failure
        </button>
      </div>

      <%!-- diff={false}: the AsyncResult swaps state wholesale (loading -> ok
      or failed), so there is nothing worth patching — send the whole
      value every time instead of a diff. --%>
      <.react name="Async" stats={@stats} diff={false} socket={@socket} />
    </div>
    """
  end

  def handle_event("reload", _params, socket) do
    {:noreply, socket |> assign(:mode, :ok) |> load_stats()}
  end

  def handle_event("fail", _params, socket) do
    {:noreply, socket |> assign(:mode, :error) |> load_stats()}
  end

  defp load_stats(socket) do
    mode = socket.assigns.mode

    assign_async(socket, :stats, fn ->
      Process.sleep(400)

      case mode do
        :ok -> {:ok, %{stats: %{stars: 1234, downloads: 98_765}}}
        :error -> {:error, "the upstream service is unavailable"}
      end
    end)
  end
end

How it works

The AsyncResult struct is passed straight to React as a prop, unchanged, because LiveReact ships an encoder for it. React reads loading, ok and failed directly off the prop rather than the server flattening them into three separate assigns and deciding which one to send.

"Reload" re-runs the same async work; "Simulate failure" reruns it in a mode that returns {:error, reason} instead, so all three states — loading, ok and failed — are reachable from the page itself.