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
defmoduleReactPhoenixWeb.SimpleGendouseGenServer @interval 1_000# Runs in Caller Context 🐌Alicedefstart_link(arg1) doGenServer.start_link(__MODULE__, arg1)enddefincrement(count) doGenServer.cast(__MODULE__, {:increment, count})end# Runs in Single Server Context 🐨Bobdefinit(arg1) do# start_link calls thisProcess.send_after(self(), :work, @interval) {:ok, arg1}enddefhandle_info(:work, state) do# send/send_after within the server calls thisProcess.send_after(self(), :increment, @interval) {:noreply, state}enddefhandle_call(:get_data,_from, state) do resp = state {:reply, resp, state}enddefhandle_cast({:increment, count}, state) do {:noreply, state+count}endend
Add to application.ex to supervise and init on startup
defstart(_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
{:ok, pid} =GenServer.start_link() #starts new linked process(one dies both die)counter =GenServer.call(pid, :get_data) #sync/blocking, default timeout 5 secIO.puts"Counter: #{counter}"GenServer.cast(pid, :increment)counter =GenServer.call(pid, :get_data)IO.puts"Counter: #{counter}
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
defmoduleSimpleGenServerMockdodefstart_link() do# runs in the caller context 🐌Alicespawn_link(__MODULE__, :init, [])enddefcall(pid, arguments) do# runs in the caller context 🐌Alice send pid, {:call,self(), arguments}receive {:response, data} -> dataendenddefcast(pid, arguments) do# runs in the caller context 🐌Alice send pid, {:cast, arguments}enddefinit() do# runs in the server context 🐨Bob initial_state =1loop(initial_state)enddefloop(state) do# runs in the server context 🐨Bobreceive 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 +1loop(new_state) endendend