Recently had to explain the difference between GenServer message types to handle TCP protocols.
Here’s a simple HelloServer
handling call
, cast
and info
messages.
defmodule HelloServer do
use GenServer
def handle_call(msg, _from, state) do
{:reply, "Received in call: #{msg}", state}
end
def handle_cast(msg, state) do
:timer.sleep 2000
IO.puts "Received in cast: #{msg}"
{:noreply, state}
end
def handle_info(msg, state) do
IO.puts "Received in info: #{msg}"
{:noreply, state}
end
end
Load this in iex
and try sending messages to the process.
$ iex hello-server.ex
iex> {:ok, pid} = GenServer.start HelloServer, []
{:ok, #PID<0.70.0>}
A call
is synchronous. It’ll return the value upon execution.
iex> GenServer.call pid, "hello"
"Received in call: hello"
The result can be assigned to a variable since the call
returns the value.
iex> result = GenServer.call pid, "hello"
iex> IO.puts result
"Received in call: hello"
It blocks the calling process.
A cast
is asynchronous. It does not block the calling process.
The VM will say “:ok
, I’ve received the message” and then process it later.
iex> GenServer.cast pid, "hello"
:ok
Received in cast: hello
I purposefully put a :timer.sleep
call to make the function sleep for 2 seconds and execute slowly. So now you see that Erlang is executing the function in the background after it returns :ok
.
The return value is :ok
and the execution happens in the background. Storing the result in a variable is less useful.
An info message is anything that is not a call
or a cast
message. All messages that are sent to a process directly (instead of via call or cast) will end up here. It is asynchronous like cast and does not block the calling process.
iex> send pid, "hello"
Received in info: hello
"hello"
send
is the Kernel.send function. It only returns the message sent.