Last active
February 14, 2024 21:22
-
-
Save brainlid/e56a1e04aa8babd8bc6b851d5a07eda5 to your computer and use it in GitHub Desktop.
Example files for a LiveView blog post that starts an async process using Phoenix Async Assigns to perform work and send message back to the LiveView. https://fly.io/phoenix-files/abusing-liveview-new-async-assigns-feature/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyAppyWeb.TaskTestLive.Index do | |
use MyAppyWeb, :live_view | |
require Logger | |
alias Phoenix.LiveView.AsyncResult | |
@impl true | |
def mount(_params, _session, socket) do | |
socket = | |
socket | |
|> assign(:async_result, %AsyncResult{}) | |
|> assign(:messages, []) | |
{:ok, socket} | |
end | |
@impl true | |
# start the async process | |
def handle_event("start", _params, socket) do | |
socket = | |
socket | |
|> assign(:messages, []) | |
|> start_test_task() | |
{:noreply, socket} | |
end | |
# cancel the async process | |
def handle_event("cancel", _params, socket) do | |
socket = | |
socket | |
|> cancel_async(:running_task) | |
|> assign(:async_result, %AsyncResult{}) | |
|> put_flash(:info, "Cancelled") | |
{:noreply, socket} | |
end | |
# handles async function returning a successful result | |
def handle_async(:running_task, {:ok, :ok = _success_result}, socket) do | |
# discard the result of the successful async function. The side-effects are | |
# what we want. | |
socket = | |
socket | |
|> put_flash(:info, "Completed!") | |
|> assign(:async_result, AsyncResult.ok(%AsyncResult{}, :ok)) | |
{:noreply, socket} | |
end | |
# handles async function returning an error as a result | |
def handle_async(:running_task, {:ok, {:error, reason}}, socket) do | |
socket = | |
socket | |
|> put_flash(:error, reason) | |
|> assign(:async_result, AsyncResult.failed(%AsyncResult{}, reason)) | |
{:noreply, socket} | |
end | |
# handles async function exploding | |
def handle_async(:running_task, {:exit, reason}, socket) do | |
socket = | |
socket | |
|> put_flash(:error, "Task failed: #{inspect(reason)}") | |
|> assign(:async_result, %AsyncResult{}) | |
{:noreply, socket} | |
end | |
@impl true | |
# handle receiving a message sent from the async process | |
def handle_info({:task_message, message}, socket) do | |
socket = | |
socket | |
|> assign(:messages, [message | socket.assigns.messages]) | |
{:noreply, socket} | |
end | |
# start the async process | |
def start_test_task(socket) do | |
live_view_pid = self() | |
socket | |
|> assign(:async_result, AsyncResult.loading()) | |
|> start_async(:running_task, fn -> | |
# the code to run async | |
Enum.each(1..5, fn n -> | |
Process.sleep(1_000) | |
IO.puts("SENDING ASYNC TASK MESSAGE #{n}") | |
# raise "TASK RAISED EXCEPTION" | |
send(live_view_pid, {:task_message, "Async work chunk #{n}"}) | |
end) | |
# return a small, controlled value because it isn't being used anyway. | |
:ok | |
# return an error result from the function | |
# {:error, "Function reporting error"} | |
end) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<.header> | |
Task Test Output | |
</.header> | |
<div class="my-4 text-center"> | |
<.button :if={!@async_result.loading} phx-click="start">Start</.button> | |
<.button :if={@async_result.loading} phx-click="cancel">Cancel</.button> | |
</div> | |
<ul id="messages" role="list" class="text-sm divide-y divide-gray-100"> | |
<li :for={msg <- @messages} class="relative flex justify-between gap-x-6 py-5 hover:bg-zinc-50 sm:rounded"> | |
<div class="font-semibold text-zinc-900"> | |
<%= msg %> | |
</div> | |
</li> | |
</ul> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyAppWeb.Router do | |
use MyAppWeb, :router | |
# ... | |
scope "/", MyAppWeb do | |
pipe_through :browser | |
get "/", PageController, :home | |
live "/task_test", TaskTestLive.Index, :index | |
end | |
# ... | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment