Skip to content

Latest commit

 

History

History
272 lines (200 loc) · 3.96 KB

File metadata and controls

272 lines (200 loc) · 3.96 KB

Elixir Koans - 15 Processes

import ExUnit.Assertions

You are a process

assert Process.alive?(self()) == ___

You can ask a process to introduce itself

information = Process.info(self())
assert information[:status] == ___

Processes are referenced by their process ID (pid)

assert is_pid(self()) == ___

New processes are spawned functions

value =
  spawn(fn ->
    receive do
    end
  end)
assert is_pid(value) == ___

Processes die when their function exits

All spawned functions are executed concurrently with the current process. You check back on slow_process and fast_process 50ms later. Let's see if they are still alive!

fast_process = spawn(fn -> :timer.sleep(10) end)
slow_process = spawn(fn -> :timer.sleep(1000) end)

:timer.sleep(50)
assert Process.alive?(fast_process) == ___
assert Process.alive?(slow_process) == ___

Processes can send and receive messages

send(self(), "hola!")
receive do
  msg -> assert msg == ___
end

A process will wait forever for a message

wait_forever = fn ->
  receive do
  end
end

pid = spawn(wait_forever)
assert Process.alive?(pid) == ___

Received messages are queued, first in first out

send(self(), "hola!")
send(self(), "como se llama?")

first_message =
  receive do
    message -> message
  end

second_message =
  receive do
    message -> message
  end
assert first_message == ___
assert second_message == ___

A common pattern is to include the sender in the message, so that it can reply

greeter = fn ->
  receive do
    {:hello, sender} -> send(sender, :how_are_you?)
  end
end

pid = spawn(greeter)

send(pid, {:hello, self()})

# ms
timeout = 100

failure_message =
  "Sorry, I didn't get the right message. Look at the message that is sent back very closely, and try again"
assert_receive ___, timeout, failure_message

Use tail recursion to receive multiple messages

def yelling_echo_loop do
  receive do
    {caller, value} ->
      send(caller, String.upcase(value))
      yelling_echo_loop()
  end
end

pid = spawn_link(&yelling_echo_loop/0)

send(pid, {self(), "o"})
assert_receive ___
send(pid, {self(), "hai"})
assert_receive ___

Processes can be used to hold state

def state(value) do
  receive do
    {caller, :get} ->
      send(caller, value)
      state(value)

    {caller, :set, new_value} ->
      state(new_value)
  end
end

initial_state = "foo"

pid =
  spawn(fn ->
    state(initial_state)
  end)

send(pid, {self(), :get})
assert_receive ___
send(pid, {self(), :set, "bar"})
send(pid, {self(), :get})
assert_receive ___

Waiting for a message can get boring

parent = self()

spawn(fn ->
  receive do
  after
    5 -> send(parent, {:waited_too_long, "I am impatient"})
  end
end)
assert_receive ___

Trapping will allow you to react to someone terminating the process

parent = self()

pid =
  spawn(fn ->
    Process.flag(:trap_exit, true)
    send(parent, :ready)

    receive do
      {:EXIT, _pid, reason} -> send(parent, {:exited, reason})
    end
  end)

receive do
  :ready -> true
end

Process.exit(pid, :random_reason)
assert_receive ___

Parent processes can trap exits for children they are linked to

Process.flag(:trap_exit, true)
spawn_link(fn -> Process.exit(self(), :normal) end)
assert_receive {:EXIT, _pid, ___}

If you monitor your children, you'll be automatically informed of their departure

spawn_monitor(fn -> Process.exit(self(), :normal) end)
assert_receive {:DOWN, _ref, :process, _pid, ___}

Next Steps