Last active
January 26, 2025 15:46
-
-
Save brainlid/c5367a9196a3d09196dbfcd14c019f02 to your computer and use it in GitHub Desktop.
Example files for a LiveView blog post that starts an async Task to perform work and send message back to the LiveView. https://fly.io/phoenix-files/star-cross-live-view-processes/
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 MyAppWeb, :live_view | |
require Logger | |
@impl true | |
def mount(_params, _session, socket) do | |
# Trap exits to catch when a Task is forcibly cancelled. | |
Process.flag(:trap_exit, true) | |
socket = | |
socket | |
|> assign(:running_task, nil) | |
|> assign(:messages, []) | |
{:ok, socket} | |
end | |
@impl true | |
def handle_event("start", _params, socket) do | |
socket = | |
socket | |
|> assign(:messages, []) | |
|> start_test_task() | |
{:noreply, socket} | |
end | |
def handle_event("cancel", _params, socket) do | |
task_id = socket.assigns.running_task | |
socket = | |
if task_id do | |
Process.exit(task_id, :kill) | |
# display it was cancelled. | |
put_flash(socket, :info, "Cancelled") | |
else | |
socket | |
end | |
{:noreply, socket} | |
end | |
@impl true | |
def handle_info({:task_message, message}, socket) do | |
socket = | |
socket | |
|> assign(:messages, [message | socket.assigns.messages]) | |
{:noreply, socket} | |
end | |
def handle_info({:EXIT, pid, _reason}, socket) do | |
socket = | |
if pid == socket.assigns.running_task do | |
# Do any cleanup needed | |
socket | |
|> assign(:running_task, nil) | |
else | |
socket | |
end | |
{:noreply, socket} | |
end | |
def start_test_task(socket) do | |
live_view_pid = self() | |
{:ok, task_pid} = | |
Task.start_link(fn -> | |
# the code to run async | |
Enum.each(1..5, fn n -> | |
Process.sleep(1_000) | |
send(live_view_pid, {:task_message, "Async work chunk #{n}"}) | |
end) | |
# function result discarded -- it isn't used | |
:ok | |
end) | |
# returning the socket so it's pipe-friendly | |
assign(socket, :running_task, task_pid) | |
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={is_nil(@running_task)} phx-click="start">Start</.button> | |
<.button :if={@running_task} 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