我們在使用 spawn 或是他的延伸 Agent,雖然很靈活,但還是有什麼都要自己來的感覺。這時候就可以使用 OTP 裡面最常用的 GenServer。
可以把它當成是底層的框架,替我們寫好了所有需要的功能,以及統一的狀態管理與接收訊息機制,最棒的是這套機制從 Erlang 開始到現在,已經是千錘百鍊的狀態,用起來額外安心。
ㄧ樣拿先前的簡單銀行帳戶為範例,我們可以在 module 裡面加上 use GenServer
來使用這個 behaviour
defmodule Bank do
use GenServer
end
就這樣直接執行的時候會跳出這個警告
warning: function init/1 required by behaviour GenServer is not implemented (in module Bank).
We will inject a default implementation for now:
def init(init_arg) do
{:ok, init_arg}
end
You can copy the implementation above or define your own that converts the arguments given to GenServer.start_link/3 to the server state.
└─ iex:1: Bank (module)
GenServer 有一系列的 callback 函式需要我們定義,首先最不能少的就是起始的狀態
defmodule Bank do
use GenServer
def init(init_balance) do
IO.puts "開戶存了 #{init_balance} 元。"
{:ok, init_balance}
end
end
GenServer 規定每個 callback 回覆的格式,在 init/1
這個 callback,我們要回傳的是由 :ok
與 GenServer 起始的狀態(以例子來說,是帳戶的初始值)所組成的 tuple:{:ok, init_balance}
使用 GenServer.start_link/3
來啟動我們定義好的 GenServer
{:ok, pid} = GenServer.start_link(Bank, 3000)
執行結果
iex(3)> {:ok, pid} = GenServer.start_link(Bank, 3000)
開戶存了 3000 元。
{:ok, #PID<0.129.0>}
下一篇我們會接收訊息,並改變狀態