*Probably* Everything you need to know about Elixir's GenServer

João Augusto Perin
3 min readJun 17, 2021

Elixir is certainly one of my favorite languages (if not the one). But I agree (and suffered a lot) with the difficulty of learning it, it is damn hard.

One of the most important topics of getting started with Elixir, is managing to get started with GenServer. Now GenServer is nothing but an abstraction of Agents, and if you want to know more about Elixir's Agentes, please check here.

That being said, let's jump right into the content.

Setup

This is a basic setup to a GenServer-based module:

This way, GenServer manages to:

  • Wrap all the methods and states declared on the module (in this case, GenServerBasics).
  • Starts a detached process, which will manage to execute all of the callbacks (we'll see it right on the next section) to get to communicate with it's parent module.

A little Caveat…

Remember that I said most call the start_link function on the parent module (e.g GenServerBasics module) just "init"?

That is because, under the hood, GenServer will call a function init when the start_link is executed, so if you want to, for instance, modify, every possible input from initial state that gets passed to a GenServer in particular, you can just override the init funcion (but remember you have the obligation of return a tuple with {:ok, <YOUR-INITIAL-STATE>}).

So, here's a simple, yet concrete example:

Here, we have the string "Hello" as the initial_state, but on init method we modify it, so our state starts being a map.

Then, we set the handle_call callback, which we'll see later on, to specify which method should we execute.

Other great point to notice is that I returned from the handle_call implementation, a 3-element-tuple, containing {:reply, my_state, my_state}. But that is not always the case. Indeed, the return correct format is at first position de atom :reply, the second position the return value from the method called by handle call (in this case, get_the_state), and the third and last one, the current state of the GenServer.

IMPORTANT: Note that the call to GenServer.call method is BLOCKING, so, if you want to get the value returned from it, you'll have to wait for the exact same handle_call implementation being called and ran so you can access the results (or assisgn'em).

But what if we wanted to set some value?

Well, we could:

I think things start to get a Little straight forward by now. We just pattern-match everything, follow the conveniences and we're great. 😄

Callbacks

Finally, the part we were all waiting for, but I'll go very simple here, 'cause it isn't and should NOT be complicated.

There are 3 callbacks on GenServer implementations:

  • handle_call: Is responsible to do the method calls synchronously, returning the actual return value from the method and the current state of the process. Remember, synchronized things are blocking things.
  • handle_cast: Is responsible to make asynchronous calls, use it when you don't need its response immediately to keep going on the code, it is very useful when you want to leverage Elixir's concurrent power.
  • handle_info: This method is fired to response any other call by any other process that IS NOT managed by GenServer implementation, there is no such thing as GenServer.info. So any other message that eventually come trough this PID, will fire handle_info (you can test this with Elixir's Process module if you want to (Process.send method)).

Queue Implementation

As a last piece of exercise, let's implement a simple Queue, with push, pop, and get functionalities, using GenServer:

Hopefully it is all explained and a little more understandable now. Trully hope it helps :)

See ya!

--

--

João Augusto Perin

I don’t know how to describe myself actually…maybe a hypothetical jedi? God no…that sounds like a bad album title from an underground 80’s band.