上一篇簡單的使用 send 與 receive 達成了基於一個 process 的狀態儲存,但我們不需要每次都自己做一個,Elixir 提供了更完整的 module 叫 Agent
與上一篇自己寫的用法類似,開啟一個 process 並給予預設值,也可以讀取與更新
{:ok, pid} = Agent.start_link(fn -> 3 end)
Agent.get(pid, fn x -> x end)
#=> 3
Agent.update(pid, fn x -> x + 3 end)
#=> :ok
Agent.get(pid, fn x -> x end)
#=> 6
在使用 Agent.start_link/2
時,我們可以加上 name
選項,可以指定名字 (使用 atom, 這邊通常用大寫類似模組名的模式),就可以不用存 pid 直接指定名字使用
Agent.start_link(fn -> 100 end, name: Storage)
Agent.get(Storage, fn x -> x end)
#=> 100
Agent.update(Storage, fn x -> x * 2 end)
#=> :ok
Agent.get(Storage, fn x -> x end)
#=> 200
Agent 使用函式來讀取或更新狀態給我們很多彈性,但有時候會想要包裝的簡單一點
以這個過度簡單的銀行範例來說,在包裝後使用上方便很多
defmodule Bank do
def start_link(initial_amount) do
Agent.start_link(fn -> initial_amount end, name: __MODULE__)
end
def balance do
Agent.get(__MODULE__, fn x -> x end)
end
def deposit(amount) do
Agent.update(__MODULE__, fn x -> x + amount end)
end
end
使用方式為
Bank.start_link(300)
Bank.balance()
#=> 300
Bank.deposit(700)
#=> :ok
Bank.balance()
#=> 1000
這裡使用 server 與 client 來指操作 Agent 的 process 與 Agent.start_link 產生的目標 (server)
依照情境可以選擇要把邏輯放在哪一端
延伸上面的銀行範例,假如每次要讀取餘額的時候,需要一個運算負擔很大的檢核函式,比起把這個函式放在 server 上,讓它變成匿名函式在 server 執行再回傳結果
def balance do
Agent.get(__MODULE__, fn x -> expensive_examine(x) end)
end
不如放在 client 端,從 server 拿到再執行
def balance do
Agent.get(__MODULE__, fn x -> x end) |> expensive_examine()
end
如此就算有多個 client,也比較不會塞在 server 這個 process 上