LoginSignup
5
0

More than 3 years have passed since last update.

【LiveView編】GenServerのhandle_info/2とProcess.send_after/4の組み合わせによる定期実行

Last updated at Posted at 2020-02-24

Process.send_after/4GenServer.handle_info/2を利用すれば定期的な処理が実行できることを先日知りました。

前回は素のGenServerで実施してみたので、今回はPhoenix LiveViewで実施してみました。

最初に結論

Phoenix LiveViewでも問題なく実行できました。
画面中では以下の2つの定期更新が行われています。
 * 1秒間隔で更新される時刻
 * 3秒ごとに更新されるFizzBuzzの値

liveview_auto_fizzbuzz_20200224_2.gif

実装

以下の環境で動作確認しました。

  • Elixir 1.9.4-otp-22
  • Phoenix Framework : 1.4.14
  • Phoexix LiveView : 0.7.1

Phoenix LiveViewはバージョンによる差異が割と出てくるので、Phoenix LiveView側の設定周りなどの説明は省略。
ただし、試した内容自体はLiveViewのバージョンにはほとんど依存しないので、0.7.1よりも古いバージョンでも動くはず。

コード

実装コードのfizzbuzz.exと画面テンプレートのautofizzbuzz.html.leexを載せます。
自分は好みでrenderの中身は外部ファイルにしていますが、今回のレベルでなら~Lを利用してfizzbuzz.ex内に記述した方が簡潔ではありますね。

lib/hello_web/live/fizzbuzz.ex
defmodule HelloWeb.Fizzbuzz do
  use Phoenix.LiveView

  import Calendar.Strftime

  def render( assigns ) do
    HelloWeb.LiveView.render("autofizzbuzz.html",assigns)
  end

  ## 補足: LiveViewが0.5.2より以前の場合は、mount/2になります。
  ##     _paramsをなくして、mount(_session, socket )になります。
  def mount(_params, _session, socket ) do
    if connected?(socket) do
      Process.send_after(self(), :tick, 1000)
      Process.send_after(self(), :fizzbuzz, 3000)
    end
    { :ok, assign( socket,
                   time: local_time(),
                   num: 0,
                   val: "initial" ) }
  end

  def handle_info(:tick, socket) do
    Process.send_after(self(), :tick, 1000)
    {:noreply, assign(socket, time: local_time())}
  end

  def handle_info(:fizzbuzz, socket) do
    Process.send_after(self(), :fizzbuzz, 3000)
    next_num = socket.assigns.num + 1
    {num, val} = fizzbuzz(next_num)
    {:noreply, assign( socket, num: num, val: val ) }
  end

  ##### local

  defp local_time() do
    :calendar.local_time() |> strftime!("%r")
  end

  defp calc_fb({n, 0, 0}), do: {n, "FizzBuzz"}
  defp calc_fb({n, 0, _}), do: {n, "Fizz"}
  defp calc_fb({n, _, 0}), do: {n, "Buzz"}
  defp calc_fb({n, _, _}), do: {n, to_string(n)}

  defp fizzbuzz(n) do
    cond do
      n < 1 -> {n , "Cannot calculate"}
      true  -> calc_fb({n, rem(n,3), rem(n, 5)})
    end
  end
end
lib/hello_web/templates/live/autofizzbuzz.html.leex
<h2>ここに現在時刻が表示されます。</h2>

<span><%= @time %></span>

<h2>ここにFizzBuzzの値が表示されます。</h2>

<span><%= @num %>:<%= @val %></span>

解説

ポイントだけ。

  • 初期画面表示時に呼ばれるmount/3中で、2つのProcess.send_afterをcallしています。
    • メッセージ「:tick」を1秒(1000ms)間隔で。
    • メッセージ「:fizzbuzz」を3秒(3000ms)間隔で。
    • なお、どちらもコネクションが確立されてる場合のみ実行。
  • メッセージ「:tick」のなかで、再びメッセージ「:tick」のhandle_info/2をcall。
  • メッセージ「:fizzbuzz」のなかで、再びメッセージ「:fizzbuzz」のhandle_info/2をcall。
  • FizzBuzzに使う数値はsocket中から取得する。
    • socket.assigns.<項目名>で取得します。
    • コード中にも記載していますが、@numを取得する場合は、socket.assigns.numと指定します。

それと、コードのコメントにも書いていますが、LiveViewのコード中で必須のmount関数が、v0.5.2まではmount/2だったのに対して、v0.6.0以降はmount/3となっています。
mount/3では第一引数にparamsを用意してやる必要がありますが、今回の例でparamsは利用していないのでバージョンの違いによる差異は感じません。

v0.5.2
v0.6.0

まとめ

GenServerがベースになっているので当たり前といえば当たり前なんですが、Phoenix LiveViewと「Process.send_after/4+handle_info/2の組み合わせ」の相性は良さげです。

参考情報

5
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
5
0