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

Elixir Processes and GenServers - Part 2

genservers_2.livemd

Elixir Processes and GenServers - Part 2

4. Tasks

Task.start/1 is similar to spawn/1. It doesn’t return a value, so use it for side-effects.

Task.start(fn -> 1 + 1 |> IO.puts() end)

The Task module also offers familiar async and await functions.

defmodule PlusOne do
  def increment(number) do
    # random wait
    :rand.uniform(1000)
    |> Process.sleep()
    
    number + 1
  end
end
calc_task = Task.async(PlusOne, :increment, [1])

IO.puts("doing something else")

result = Task.await(calc_task)
IO.puts(result)

A Task is intended for doing one thing and then finishing. You can start multiple tasks and await the result.

# stream = Task.async_stream(1..10, PlusOne, :increment, [])

1..10
|> Task.async_stream(PlusOne, :increment, [], ordered: false)
|> Enum.map(fn {:ok, num} -> num end)

In this example, Task.async_stream() is passed a range (list of numbers from 1 to 10). Each number is incremented in a separate process. It returns an Enumberable of {:ok, result} tuples. We can use the Enum.map() to turn this into a list of numbers.

5. Agents

An agent is intended to be longer running and typically maintains state.

{:ok, agent_pid} = Agent.start(fn -> %{} end)
Agent.update(agent_pid, fn state -> Map.put(state, :foo, 1) end)
Agent.update(agent_pid, fn state -> Map.put(state, :bar, 2) end)

foo_value = Agent.get(agent_pid, fn state -> Map.get(state, :foo) end)
IO.puts("foo: #{foo_value}")

full_state = Agent.get(agent_pid, fn state -> state end)
IO.inspect(full_state, label: :state)

If Agent.update cannot update the agent before a timeout, it will raise an error.

However, Agent.cast will fire and forget. No error is raised.

Agent.cast(agent_pid, fn state -> Map.put(state, :foo, 7) end)
Agent.get(agent_pid, fn state -> state end) |> IO.inspect(label: :state)

# Let's stop the agent
Agent.stop(agent_pid)

# Now send something to the missing agent.
Agent.cast(agent_pid, fn state -> Map.put(state, :foo, "oops") end)

Next