GenServer
One of the most widely used OTP abstractions is GenServer. A GenServer (generic server) provides a standardized interface for handling sync and async requests and managing state.
defmodule Counter do
use GenServer
def init(initial_value) do
{:ok, initial_value}
end
# sync calls
def handle_call(:get_count, _from, state) do
{:reply, state, state}
end
# async calls
def handle_cast(:incr, state) do
{:noreply, state + 1}
end
def handle_cast(:decr, state) do
{:noreply, state - 1}
end
end
Use the Counter:
GenServer.start_link(Counter, 0, name: Counter)
GenServer.cast(Counter, :incr)
GenServer.call(Counter, :get_count) # 1
GenServer.cast(Counter, :decr)
GenServer.call(Counter, :get_count) # 0
Add helper functions
defmodule Counter do
use GenServer
def init(initial_value) do
{:ok, initial_value}
end
# sync calls
def handle_call(:get_count, _from, state) do
{:reply, state, state}
end
# async calls
def handle_cast(:incr, state) do
{:noreply, state + 1}
end
def handle_cast(:decr, state) do
{:noreply, state - 1}
end
# helper functions
def incr, do: GenServer.cast(__MODULE__, :incr)
def decr, do: GenServer.cast(__MODULE__, :decr)
def get_count, do: GenServer.call(__MODULE__, :get_count)
def start_link(initial_value) do
GenServer.start_link(__MODULE__, initial_value, name: __MODULE__)
end
end
Now we can have better APIs:
Counter.start_link(0)
Counter.incr
Counter.get_count
Counter.decr
Counter.get_count
GenServers are a higher-level abstraction than processes and can be used with Supervisors.