3
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?

GenServerとは何でどういう時に使うのか?

Posted at

はじめに

今回はGenServerとは何か? どのような時にGenServerを使うのか?を解説します。

関連テーマである。Webソケット通信の復習はこちらから↓

GenServerとは

GenServerはWebSocketとは別のものです。
GenServerはあくまでeditor(構造体)のことを指します。
データを取得するときはGenServerに登録した時と同じpidを使用する必要があります。
そのpidは元々sessionが持っている状態ですが、mount/3以外からsessionにアクセスすることができません。
そこで、sessionが持っているpidをsocketに登録し、他のLiveViewのコールバック関数からアクセスするという使い方をします。

参考hexdocs: GenServer

どのような時にGenServerを使うのか?

LiveViewは2回mountを行います。2回のmountで違うデータを取得してしまい、データの整合性が取れなくなってしまう時に、1回目のmountで取得したデータをGenServerに登録し、2回目のmount時にも使うようにします。

具体的には、ランダムに練習問題を生成するような学習アプリにおいて1回目のmountでランダムに取得した練習問題をGenServerに登録し2回目のmountはGenServerから取得し、socketにasaignして以後使用します。(GenServerで登録しないと1回目と2回目で取得する問題が変わってしまうため)

データの流れ

  • 1回目のmountでDBから必要な値を取得し、editor構造体を作る
  • 作ったeditoer構造体をGenServerのストレージに入れる
  • 2回目のmountの時にGenServerに登録したeditor構造体をとってくる

LiveViewのライフサイクル

LiveViewのライフサイクルが理解できていないとなぜこのようなことをしなければいけないのか?がわからないと思うので、参考の図解も貼っておきます。

IMG_0173.jpg

参考: Elixir実践入門 第10章 LiveViewによるフロントエンドの開発

処理の流れ

まずは日本語で

  • 1回目のmount/3でsessionpidとそれに紐づくeditors構造体をGenServerに登録
  • 2回目のmount/3でGenServerから登録したpidとeditors構造体を取得
  • 取得したデータをsocketasignする
  • Webソケットが切断され、terminate/2が呼ばれるのでsocketasignしたpidとeditors構造体をGenServerに登録
  • 再度接続が復活した時に、2回目のmount/3が呼ばれるのでGenServerから登録したpidとeditors構造体を取得

コードベースの解説

mountでのpidをsetする処理とgetする処理

  def mount(%{"log_id" => log_id}, session, socket) when socket.assigns.live_action === :random do
    editor =
      if connected?(socket) do
       # LiveViewの接続が確認できているので2回目のmount
        UseQuestion.get_editor(session["question_pid"])
      else
       # LiveViewの接続が確認できていないので1回目のmount
        log = Logs.get_incomplete_mb_ques_log(log_id)

        editor =
          socket.assigns.current_user
          |> MbQuesEditor.construct(log)
          |> MbQuesEditor.update_q_task_times(0, :start)

        UseQuestion.set_editor(session["question_pid"], editor)

        editor
      end

    {:ok, assign_use(socket, editor, session)}
  end

mountで取得したデータをsocketにasignする

  defp assign_use(socket, editor, session \\ %{}) do
    socket
    |> assign(:editor, editor)
    |> assign(:set_submit, false)
    |> assign(:button_disabled, true)
    |> assign(:button_class, "bg-gray-30")
    |> assign(:set_textbook, false)
    |> assign(:current_index, 0)
    |> assign(:set_exit, false)
    |> assign(:set_page, false)
    |> assign(:page, nil)
    |> assign(:question_pid, session["question_pid"])
    |> push_event("start_message", %{})
  end

中断の場合GenServerに該当するpidのデータを更新して保存する

  @impl true
  def terminate(_reason, socket) do
    # 中断の場合はGenserverに値を保存しておく
    unless socket.assigns.editor.log.completed && socket.assigns.live_action == :answers do
      UseQuestion.set_editor(socket.assigns.question_pid, socket.assigns.editor)
    end
    :ok
  end

GenServerの値をsetとget


  @doc """
  GenServerに保存しているeditorを取得します。

  ## Examples

      iex> get_editor(pid)
      %{}
  """
  @spec get_editor(pid()) :: map()
  def get_editor(pid) do
    GenServer.call(pid, :get_editor)
  end

  @doc """
  GenServerにログとエディターを保存します。

  ## Examples

      iex> set_log_and_editors(pid, editor)
      :ok
  """
  @spec set_editor(pid(), map()) :: :ok
  def set_editor(pid, editors) do
    GenServer.cast(pid, {:set_log_and_editors, editors})
  end
end

コードの補足

connectedとは

2回目のmountを確認しています。(Webソケットの接続)
これだけだとよくわからないと思うので順を追って解説します。

1回目のmountはHTTP接続および画面作成のDOM生成(テンプレート作成)。2回目はWebSocketに接続しましたという通知を送る役割で行います。
つまり、connectedはWebSocketに接続しているかを確認するヘルパー関数なので実質的には2回目のmountかどうかを確認していることになります。

かなり拙い図解ですが...。
スクリーンショット 2024-05-09 16.23.15.png

参考hexdocs: Phoenix.LiveView.connected/2

terminate/2とは

ページ終了時、ブラウザバック、Webソケットが切断された時に呼ばれる

参考hexdocs: Phoenix.LiveView.terminate/2

参考hexdocs: GenServer.terminate/2

まとめ

GenServerは名前から別のサーバーのように認識していましたが、構造体ということがわかりました。

3
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
3
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?