Help us understand the problem. What is going on with this article?

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

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の組み合わせ」の相性は良さげです。

参考情報

MzRyuKa
のんびりごろごろ、ネコ、うさまる、技術の話は大好きです。 仕事では主にRubyを利用している。最近は、バッチ周りはgolangやPythonに置き換え中。 技術書典7では「PhoenixLiveViewとNervesをさわるElixirへのいざない」と言うタイトルのElixirの技術同人誌を書きました。
https://mzryuka.hatenablog.jp/
fukuokaex
エンジニア/企業向けにElixirプロダクト開発・SI案件開発を支援する福岡のコミュニティ
https://fukuokaex.fun/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした