En Elixir, todo el código se ejecuta dentro de procesos. Los procesos de Elixir:
De forma predeterminada, una función se ejecuta en el mismo proceso desde el que se llamó. Cuando necesites ejecutar explícitamente una función concreta en un proceso nuevo, usa spawn:
spawn/1 acepta una función que ejecutará directamente.
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 acepta una función que ejecutará indicando el nombre del módulo, el nombre de la función (como átomo) y una lista de argumentos que se pasan a esa función.
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
Un proceso finaliza en cuanto su función ha terminado de ejecutarse.
Puedes comprobar si un proceso sigue vivo (ejecutándose) con Process.alive?/1:
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
Los procesos no comparten información directamente entre sí. Los procesos envían mensajes para compartir datos. Este patrón de concurrencia se llama modelo de actores.
Envía mensajes a un proceso con send/2.
send(pid, :hello)
send no comprueba si el mensaje se ha recibido ni si el destinatario sigue vivo.Un mensaje puede ser de cualquier tipo.
Puedes recibir un mensaje enviado al proceso actual con receive/1.
receive espera hasta que haya en el buzón del proceso un mensaje que coincida con alguno de los patrones dados.
after._ que abarque todo en receive/1 para evitar quedarte sin memoria por la acumulación de mensajes no leídos.receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
Si quieres recibir más de un mensaje, tienes que llamar a receive/1 de forma recursiva. Es un patrón habitual implementar una función recursiva, por ejemplo llamada loop, que llama a receive/1, hace algo con el mensaje y luego se llama a sí misma para esperar más mensajes. Si necesitas arrastrar algo de estado de una llamada a receive/1 a otra, puedes hacerlo pasando un argumento a esa función loop.
def loop(state) do
receive do
:increment_by_one ->
loop(state + 1)
{:report_state, sender_pid} ->
send(sender_pid, state)
loop(state)
:stop ->
nil
_ ->
loop(state)
end
end
En la práctica, este enfoque rara vez se usa directamente. Elixir ofrece abstracciones de concurrencia, como el módulo Agent o un comportamiento GenServer, que se construyen sobre el bucle de recepción. Sin embargo, es fundamental entender esos conceptos básicos para poder usar las abstracciones de forma eficiente.