Processos em Elixir
Tags: elixir, erlang, tutorial, concurrency
Published: 2020-11-28 18:20:19

# Para quem é este texto?
Este texto vai ser um pouco mais teórico, então se você está buscando entender melhor sobre como os _processos_ em Elixir funcionam, continue lendo...


# Por que estudar processos?
Como diria _Joe Armstrong_, um dos criadores do Erlang:
```"O Erlang foi projetado desde o começo para ser uma linguagem de programação funcional, que permitisse a criação de programas concorrentes e distribuídos." - citado em Programming Erlang, Software for a concurrent world.```

E assim como Erlang, o Elixir também possui essas mesmas características, e lidar com **programas concorrentes** tem se tornado cada vez mais uma necessidade real no mundo do Desenvolvimento de Software.

Por isso, lidar com processos e suas abstrações no Elixir é um tópico extremamente importante, não só para conseguirmos aprimorar os nossos projetos, mas também para entender como a linguagem, frameworks e bibliotecas disponíveis nesse ecossistema funcionam.


# Por onde começar?

Elixir atualmente possui diversos tipos de abstrações de processos, como por exemplo: **Tasks, Agents, GenServers e Supervisors**... Onde cada abstração possui a sua própria responsabilidade definida.

É importante estar por dentro deste mundo de abstrações de processos desde a parte mais básica - utilizando spawn/receive - até a criação de processos com GenServers e Supervisors, já que estamos falando sobre uma linguagem de programação concorrente.

Supondo que precisamos criar um módulo que será responsável por tratar detalhes específicos da funcionalidade do nosso produto, que não necessariamente precisam ser executados de forma síncrona, como um disparador de emails, ou até mesmo um disparador de tokens para um sistema 2FA ([2 Factor Authentication](https://en.wikipedia.org/wiki/Multi-factor_authentication)). 

Ambos são exemplos de aplicações boas para ter um processo secundário rodando.

Mas como eu posso começar a trabalhar com processos?


## Iniciando com `spawn/1`
Uma das formas que tem funcionado muito bem para mim tem sido tentar entender como funcionavam os processos em Elixir desde o mais básico, utilizando somente `spawn/1`.

Dito isso, vamos começar com um exemplo:

Digamos que eu precise criar um processo que irá esperar uma mensagem e disparar um `IO.inspect/1` (Função de inspecionar o elemento) com os argumentos recebidos.
```elixir
# módulo que irá disparar as mensagens
defmodule DispatchMessage do
  @moduledoc """
  Process that will await for a message, dispatch when
  received and terminate. 
  """

  @spec start_link() :: pid()
  def start_link() do
    spawn(&wait_message/0)
  end

  defp wait_message() do
    receive do
      {:inspect, data} -> IO.inspect(data)
    end
  end
end
```

Ele irá executar uma simples atividade de forma assíncrona e encerrar.

Podemos checar isso da seguinte forma:
```elixir
iex> pid = DispatchMessage.start_link()
#PID<0.119.0>

iex> Process.alive? pid
true

iex> send pid, {:inspect, %{hello: :world}}
%{hello: :world}

iex> Process.alive? pid
false
```

Podemos ver com o `Process.alive?/1` que o processo foi encerrado após executar a sua task de `IO.inspect(data)`.


Mas o que exatamente aconteceu quando executamos este trecho de código no _iex_?

O `DispatchMessage.start_link/0` retornou um PID (Process ID), que é o que usaremos para nos comunicar com o processo que acabamos de criar.

Já o `send/2` é a função que usamos para enviar uma mensagem para o processo (PID) que criamos. Este mesmo processo já estava esperando receber uma mensagem a partir do trecho: `wait_message/0`.


# Ciclo de vida

Existem alguns detalhes importantes para mencionar sobre o ciclo de vida desses tipos de processos.
- Os processos criados a partir do `spawn/1` por padrão irão executar uma task simples e encerrar.
- Apesar de estarmos paralelizando o projeto utilizando processos, nosso processo só será capaz de executar um comando por vez.
- Chamadas em excesso podem fazer com que o processo precise enfileirar as demais chamadas e isso pode causar _timeout_ em chamadas síncronas (pode acontecer se você estiver usando GenServer `handle_call/3`).
- Podemos determinar se esse processo está esperando por um input utilizando a estrutura de `receive`.

```elixir
iex> pid = spawn(fn -> 
...>   receive do 
...>     {:msg, msg} -> IO.puts("Recebi uma mensagem #{msg}")
...>   end
...> end)

iex> send pid, {:msg, "oi"}
"Recebi uma mensagem oi"
```

Podemos definir também loops e até mesmo simular um estado interno para processos desse tipo, por exemplo:

```elixir
defmodule StatefulProcess do
  @moduledoc """
  Stateful process abstraction using spawn/1
  """

  @spec start_link() :: pid()
  def start_link() do
    spawn(&loop/0)
  end

  @spec add_data(pid(), map()) :: tuple()
  def add_data(pid, data) do
    send pid, {:msg, data}
  end

  @spec list(pid()) :: atom()
  def list(pid), do: send pid, :list

  defp loop(), do: loop([])
  defp loop(state) do
    receive do
      {:msg, map_msg} ->
        state ++ [map_msg]
      :list ->
        IO.inspect(state)
        state
    end
    |> loop()
  end
end
```

Acabamos de criar um módulo de processo com estado interno que irá iniciar vazio e incrementará a lista de estado a partir das chamadas `add_data/2`

ex:
```elixir
iex> pid = StatefulProcess.start_link
#PID<0.231.0>

iex> StatefulProcess.add_data pid, %{hello: :world}
{:msg, %{hello: :world}}

iex> StatefulProcess.list(pid)
[%{hello: :world}]
:list
```

A grande sacada deste novo módulo é que quando iniciarmos o processo pelo `c:start_link/0`, o mesmo não irá terminar após alguma execução (seja ela: `c:add_data/2` ou `c:list/1`), além de que ele também irá armazenar o estado atualizado a partir do `c:add_data/2`.



# Conclusão

Perceba a quantidade de possibilidades que temos somente com a implementação mais básica de processos em Elixir: imagine o que podemos fazer usando as demais abstrações acima!?

A ideia deste texto era desmistificar processos com Elixir, mostrar algumas abordagens usando `spawn/1` e `receive-do` e destacar alguns pontos importantes sobre ciclo de vida dentro da linguagem.


Os tópicos abordados neste texto podem servir para os demais tipos de processos. Pretendo escrever mais sobre as outras abstrações!


Até mais...

![cat bye giphy](https://media.giphy.com/media/tuvMgAPzxaQBq/giphy.gif)