Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Elixir School - Intermediate

elixir_school_intermediate.livemd

Elixir School - Intermediate

Concurrency

Processes in the Erlang VM are lightweight and can be ran across all available CPUs of the machine it is being ran on

defmodule Example do
  def add(a, b) do
    IO.puts(a + b)
  end
end
Example.add(2, 3)

To create a new process in the Erlang VM, use the spawn/1 or spawn/3 functions, which return a Process Identifier (PID)

spawn(Example, :add, [2, 3])

Message Passing

For two processes to communicate, the functions send/2 and receive/1 allow the programmer to create this communication logic

defmodule Example do
  def listen do
    receive do
      {:ok, "hello"} -> IO.puts("World")
    end

    listen()
  end
end
pid = spawn(Example, :listen, [])
send(pid, {:ok, "hello"})
send(pid, :ok)
Process.exit(pid, :normal)

Process Linking

spawn_link/1, or spawn_link/3, can be used to “link” two processes so they can be “aware” of each other

Two linked processes will receive exit notifications from one another

defmodule Example do
  def explode, do: exit(:kaboom)
end
# spawn(Example, :explode, [])
# spawn_link(Example, :explode, [])
defmodule Example do
  def explode, do: exit(:kaboom)

  def run do
    Process.flag(:trap_exit, true)
    spawn_link(Example, :explode, [])

    receive do
      {:EXIT, _from_pid, reason} -> IO.puts("Exit reason: #{reason}")
    end
  end
end
Example.run()

Process Monitoring

spawn_monitor/1, or spawn_monitor/3, can be used to be notified when a process crashes without the current process crashing or needing to explicitly trap exits

defmodule Example do
  def explode, do: exit(:kaboom)

  def run do
    spawn_monitor(Example, :explode, [])

    receive do
      {:DOWN, _ref, :process, _from_pid, reason} -> IO.puts("Exit reason: #{reason}")
    end
  end
end
Example.run()

Agents

An abstraction around background processes maintaining state

{:ok, agent} = Agent.start_link(fn -> [1, 2, 3] end)
Agent.update(agent, fn state -> state ++ [4, 5, 6] end)
Agent.get(agent, & &1)
Agent.start_link(fn -> [1, 2, 3] end, name: Numbers)
Agent.get(Numbers, & &1)

Tasks

Provides a way to execute a function in the background and retrieve its value later

Useful when handling expensive operations without blocking the application execution

defmodule Example do
  def double(x) do
    :timer.sleep(2000)
    x * 2
  end
end
task = Task.async(Example, :double, [2000])
Task.await(task)