0Pricing
Elixir & Phoenix: Scalable Backend Development · Leçon

Implémentation du comportement GenServer

Maîtrisez le comportement `GenServer` pour créer des processus serveur robustes, avec état, capables de traiter les requêtes de manière asynchrone.

Implémentation du comportement GenServer est une leçon Elixir & Phoenix: Scalable Backend Development gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Elixir & Phoenix: Scalable Backend Development, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Elixir & Phoenix: Scalable Backend Development comprend 4 leçons au total.

Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.

Meet the GenServer

Welcome to GenServer, a core building block in Elixir for creating robust, stateful processes!

You've learned about basic Elixir processes and message passing. GenServer is a special type of process that provides a standardized way to manage state and handle requests, making concurrent programming much easier.

It's part of Elixir's OTP (Open Telecom Platform) behaviors, offering fault tolerance and a clear structure for your server-like processes.

Why Use GenServer?

Using a GenServer offers several key advantages:

  • Standardized API: It provides a consistent interface for interacting with processes.
  • State Management: It simplifies holding and updating data over time within a process.
  • Fault Tolerance: When used with supervisors (covered later!), GenServers can be automatically restarted if they crash.
  • Concurrency: It allows multiple clients to interact with a single process safely.

Think of it as a reliable, single-threaded server for your data or operations.

Starting a GenServer: init/1

Every GenServer begins its life by calling GenServer.start_link/3, which then invokes its init/1 callback function. This is where you set up the initial state of your server.

Let's see a basic example:

defmodule MyGenServer do
  use GenServer

  # Client API: How others interact with our GenServer
  def start_link(initial_state) do
    GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
  end

  # Server Callbacks: What the GenServer does internally
  @impl true
  def init(initial_state) do
    IO.puts "[GenServer] Initializing with: #{inspect initial_state}"
    {:ok, initial_state} # Return {:ok, state} to indicate success
  end
end

# --- Main execution block for runnable example --- 
# Start the GenServer with 'Hello Elixir!' as its initial state
{:ok, pid} = MyGenServer.start_link("Hello Elixir!")
IO.puts "[Main] GenServer started with PID: #{inspect pid}"

# For a simple script, we just start it. In a real app, 
# it would be supervised and run in the background.

Getting Replies: handle_call

When you need to send a request to a GenServer and wait for a reply, you use GenServer.call/2. The GenServer handles this with the handle_call/3 callback.

This is a synchronous operation: your code waits until the GenServer processes the request and sends back a response.

defmodule MyGenServer do
  use GenServer

  # Client API
  def start_link(initial_state) do
    GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
  end

  def get_state do
    GenServer.call(__MODULE__, :get_state)
  end

  # Server Callbacks
  @impl true
  def init(initial_state) do
    {:ok, initial_state}
  end

  @impl true
  def handle_call(:get_state, _from, current_state) do
    IO.puts "[GenServer] Handling :get_state call."
    # {:reply, response_to_client, new_server_state}
    {:reply, current_state, current_state}
  end
end

# --- Main execution block ---
MyGenServer.start_link("Initial Data")
state = MyGenServer.get_state()
IO.puts "[Main] Received state from GenServer: #{inspect state}"

Fire & Forget: handle_cast

Sometimes you just want to send a message to a GenServer without waiting for a reply. This is where GenServer.cast/2 comes in handy, handled by the handle_cast/2 callback.

This is an asynchronous operation: your code sends the message and immediately continues, without waiting for the GenServer to process it.

defmodule MyGenServer do
  use GenServer

  # Client API
  def start_link(initial_state) do
    GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
  end

  def get_state do
    GenServer.call(__MODULE__, :get_state)
  end

  def set_state(new_state) do
    GenServer.cast(__MODULE__, {:set_state, new_state})
  end

  # Server Callbacks
  @impl true
  def init(initial_state) do
    {:ok, initial_state}
  end

  @impl true
  def handle_call(:get_state, _from, current_state) do
    {:reply, current_state, current_state}
  end

  @impl true
  def handle_cast({:set_state, new_state}, _current_state) do
    IO.puts "[GenServer] Handling {:set_state, ...} cast."
    # {:noreply, new_server_state}
    {:noreply, new_state}
  end
end

# --- Main execution block ---
MyGenServer.start_link("Initial Data")
IO.puts "[Main] Current state: #{MyGenServer.get_state()}"
MyGenServer.set_state("Updated Data")
Process.sleep(10) # Give cast a moment to process
IO.puts "[Main] New state after cast: #{MyGenServer.get_state()}"

GenServer's Internal State

The 'state' of a GenServer is just an Elixir term (like a number, map, or list) that is passed around between its callback functions.

  • init/1 returns the initial state.
  • handle_call/3 returns the current state as its third element.
  • handle_cast/2 returns the current state as its second element.

Each callback receives the current state as an argument and returns the new state. This simple mechanism allows your GenServer to maintain and update its internal data reliably.

Practical Example: A Counter

Let's put handle_call and handle_cast together to build a simple counter GenServer. It will allow us to increment the counter (async) and get its current value (sync).

defmodule Counter do
  use GenServer

  # Client API
  def start_link do
    GenServer.start_link(__MODULE__, 0, name: __MODULE__)
  end

  def increment do
    GenServer.cast(__MODULE__, :increment)
  end

  def get_count do
    GenServer.call(__MODULE__, :get_count)
  end

  # Server Callbacks
  @impl true
  def init(initial_count) do
    IO.puts "[Counter] Initialized to: #{initial_count}"
    {:ok, initial_count}
  end

  @impl true
  def handle_cast(:increment, current_count) do
    new_count = current_count + 1
    IO.puts "[Counter] Incremented to: #{new_count}"
    {:noreply, new_count}
  end

  @impl true
  def handle_call(:get_count, _from, current_count) do
    IO.puts "[Counter] Returning current count: #{current_count}"
    {:reply, current_count, current_count}
  end
end

# --- Main execution block ---
Counter.start_link()
IO.puts "[Main] Initial count: #{Counter.get_count()}"
Counter.increment()
Counter.increment()
Process.sleep(10) # Give casts time to process
IO.puts "[Main] Count after increments: #{Counter.get_count()}"

Beyond Call & Cast

While handle_call and handle_cast are the most common, GenServers have other callbacks:

  • handle_info/2: For handling general messages sent directly to the process (e.g., using send/2).
  • terminate/2: For cleanup code when the GenServer is about to stop.
  • code_change/3: For handling hot code upgrades without stopping the server.

These callbacks provide even more control over the GenServer's lifecycle and behavior.

GenServer Best Practices

To make your GenServers effective and maintainable, consider these tips:

  • Separate Client API: Keep the start_link, call, and cast functions separate from the @impl callbacks.
  • Small State: Try to keep the GenServer's internal state minimal and simple.
  • Avoid Long Operations: Don't perform time-consuming tasks directly in callbacks; this can block other requests. Use Elixir's Task module for such work.
  • Use Names: Register your GenServers with names (e.g., name: __MODULE__) for easy lookup across your application.

GenServer Check-up

Time for a quick question to check your understanding of GenServers!

GenServer: Key Takeaways

You've mastered the fundamentals of GenServer behavior!

  • GenServers are stateful processes that manage data and handle requests.
  • init/1 sets up the initial state.
  • GenServer.call/2 (handled by handle_call/3) is for synchronous requests that expect a reply.
  • GenServer.cast/2 (handled by handle_cast/2) is for asynchronous, 'fire-and-forget' messages.
  • The GenServer's state is passed between callbacks, allowing it to evolve over time.

GenServers are a powerful tool for building concurrent and fault-tolerant applications in Elixir.

Questions Fréquemment Posées

La leçon « Implémentation du comportement GenServer » est-elle gratuite ?

Oui — le texte complet de « Implémentation du comportement GenServer » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Elixir & Phoenix: Scalable Backend Development, passe à CoddyKit PRO. Le cours Elixir & Phoenix: Scalable Backend Development comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Implémentation du comportement GenServer » ?

Maîtrisez le comportement `GenServer` pour créer des processus serveur robustes, avec état, capables de traiter les requêtes de manière asynchrone. Tu pratiques Elixir & Phoenix: Scalable Backend Development avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Elixir & Phoenix: Scalable Backend Development ?

Aucune expérience préalable n'est requise. Elixir & Phoenix: Scalable Backend Development sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Implémentation du comportement GenServer » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Elixir & Phoenix: Scalable Backend Development ?

Oui. Chaque leçon Elixir & Phoenix: Scalable Backend Development inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Processus Elixir et transmission de messages
  2. Implémentation du comportement GenServer
  3. Superviseurs et structure des applications
  4. Travail concurrent avec Task et Agent
← Retour à Elixir & Phoenix: Scalable Backend Development