Start seperate process that holds some state, changes its state based on calls(sync expecting response) or casts(async, no resp)
Can recieve other/more messages than agent with handle_info
GenServer is a single process, so best to minimize server work and only handle scheduling, use Task delegation, or offload most work to client caller
react_phoenix_web/simplegen.ex
defmodule ReactPhoenixWeb.SimpleGen do
use GenServer
@interval 1_000
# Runs in Caller Context 🐌Alice
def start_link(arg1) do
GenServer.start_link(__MODULE__, arg1)
end
def increment(count) do
GenServer.cast(__MODULE__, {:increment, count})
end
# Runs in Single Server Context 🐨Bob
def init(arg1) do
# start_link calls this
Process.send_after(self(), :work, @interval)
{:ok, arg1}
end
def handle_info(:work, state) do
# send/send_after within the server calls this
Process.send_after(self(), :increment, @interval)
{:noreply, state}
end
def handle_call(:get_data, _from, state) do
resp = state
{:reply, resp, state}
end
def handle_cast({:increment, count}, state) do
{:noreply, state+count}
end
end
Add to application.ex to supervise and init on startup
def start(_type, _args) do
# List all child processes to be supervised
children = [
#....
{ReactPhoenixWeb.SimpleGen, 1}
]
#....
Then just call with ReactPhoenixWeb.SimpleGen.increment(5)
Raw Examples
start_link(module, init_arg, options \\ []) calls init of the module with the init_args
Don't use Process.sleep, instead will have to store data in state, use :timer.after to send message after time and process/complex work in handler
Raw Example without GenServer
defmodule SimpleGenServerMock do
def start_link() do
# runs in the caller context 🐌Alice
spawn_link(__MODULE__, :init, [])
end
def call(pid, arguments) do
# runs in the caller context 🐌Alice
send pid, {:call, self(), arguments}
receive
{:response, data} -> data
end
end
def cast(pid, arguments) do
# runs in the caller context 🐌Alice
send pid, {:cast, arguments}
end
def init() do
# runs in the server context 🐨Bob
initial_state = 1
loop(initial_state)
end
def loop(state) do
# runs in the server context 🐨Bob
receive command do
{:call, pid, :get_data} ->
# do some work on data here and update state
{new_state, response} = {state, state}
send pid, {:response, data}
loop(new_state)
{:cast, :increment} ->
# do some work on data here and update state
new_state = state + 1
loop(new_state)
end
end
end