Skip to content

Instantly share code, notes, and snippets.

@brainlid
Last active November 14, 2023 17:23
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brainlid/c5367a9196a3d09196dbfcd14c019f02 to your computer and use it in GitHub Desktop.
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/
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
<.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>
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