8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LiveViewの非同期処理を検討してみる

Last updated at Posted at 2025-12-05

このコラムはページ上で非同期処理の実装を検討します

  • assign_async
  • Task.start_link
    を検討したいと思います

実行イメージ

assign_async

Task.start_link

処理イメージ(共通)

  • 実行ボタンを押す
    • 実行後実行ボタンを押せなくする
  • 処理中を再現する
    • GUIと別のプロセスにする
    • 擬似的に進行中を作る
      • GUI側のプロセスに進捗を通知する
  • 完了時通知
    - GUI側のプロセスに完了通知する
    - 実行後実行ボタンを押せるようにする

サンプルソース

assign_async

lib/live_async_web/live/index.ex
defmodule LiveAsyncWeb.SampleLive.Index do
  use LiveAsyncWeb, :live_view

  def mount(_params, _session, socket) do
    socket =
      assign(socket, text: "実行ボタンを押してください")
      |> assign(btn: true)
      |> assign(ret: nil)

    {:ok, socket}
  end

  def handle_event("start", _, socket) do
    pid = self()

    socket =
      assign(socket, btn: false)
      |> assign(ret: nil)
      |> assign_async(:ret, fn -> run(pid) end)

    {:noreply, socket}
  end

  def run(pid) do
    1..100
    |> Enum.each(fn x -> run_sub(x, pid) end)

    Process.send(pid, {:end, "完了しました"}, [])
    {:ok, %{ret: Enum.random(1..10)}}
  end

  def run_sub(no, pid) do
    Process.sleep(20)
    Process.send(pid, {:run, "処理中#{no} %"}, [])
  end

  def handle_info({:run, msg}, socket) do
    {:noreply, assign(socket, text: msg)}
  end

  def handle_info({:end, msg}, socket) do
    socket =
      assign(socket, text: msg)
      |> assign(btn: true)

    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <Layouts.flash_group flash={@flash} />
    <div class="p-5">
      <button disabled={!@btn} class="btn" phx-click="start">実行</button>
      <p class="m-2">{@text}</p>
      <p class="m-2">{inspect(@ret)}</p>
    </div>
    """
  end
end
  • def handle_event("start", _, socket)
    • ボタンを押された時
    • assign_async(:ret, fn -> run(pid) end) で非同期処理
      • pidはGUI側のpid
  • def run(pid)
    • 100回run_sub(no, pid)を呼ぶ
    • 完了時にProcess.send(pid, {:end, "完了しました"}, [])
      • GUI側に通知します
  • def run_sub(no, pid)
    • 20ミリ待つ
    • 処理中を再現でProcess.send(pid, {:run, "処理中#{no} %"}, [])
      • GUI側に通知します
  • def handle_info({:run, msg}, socket)
    • 処理中の通知を受け取る
  • def handle_info({:end, msg}, socket)
    • 完了時の通知を受け取る

Task.start_link

lib/live_async_web/live/async.ex
defmodule LiveAsyncWeb.AsyncTest do
  use LiveAsyncWeb, :live_view

  def render(assigns) do
    ~H"""
    <Layouts.flash_group flash={@flash} />
    <div class="p-5">
      <button disabled={!@btn} class="btn" phx-click="start">実行</button>
      <p class="m-2">{@text}</p>
    </div>
    """
  end

  def mount(_params, _session, socket) do
    socket =
      assign(socket, text: "実行ボタンを押してください")
      |> assign(btn: true)

    {:ok, socket}
  end

  def handle_event("start", _unsigned_params, socket) do
    pid = self()
    Task.start_link(fn -> run(pid) end)

    socket = assign(socket, btn: false)

    {:noreply, socket}
  end

  def run(pid) do
    1..100
    |> Enum.each(fn x -> run_sub(x, pid) end)

    Process.send(pid, {:end, "完了しました"}, [])
    :ok
  end

  def run_sub(no, pid) do
    Process.sleep(20)
    Process.send(pid, {:run, "処理中#{no} %"}, [])
  end

  def handle_info({:run, msg}, socket) do
    socket = assign(socket, text: msg)
    {:noreply, socket}
  end

  def handle_info({:end, msg}, socket) do

    socket =
      assign(socket, text: msg)
      |> assign(btn: true)

    {:noreply, socket}
  end
end

差分

  • def handle_event("start", _unsigned_params, socket) do
    • Task.start_link(fn -> run(pid) end)

ぐらいです

厳密にはassign_asyncの方は直接実行結果を渡すことが出る

どっちを使う?

  • assign_asyncで良いのでは
  • そもそも非同期そんなに長い処理書きませんよね
  • ベージ閉じても実行したい場合はこの2つの方法以外を考える必要がある
  • assign_asynが使えないバージョンならTask.start_linkで再現もありかも
8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?