في Elixir، يعمل كل الكود داخل العمليات. عمليات Elixir:
افتراضيًا، تُنفَّذ الدالة في العملية نفسها التي استُدعيت منها. وعندما تحتاج إلى تشغيل دالة معينة في عملية جديدة صراحةً، استخدم spawn:
spawn/1 يقبل دالة ينفّذها مباشرةً.
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 يقبل دالة ينفّذها باسم الوحدة، واسم الدالة (كذرّة)، وقائمة من الوسائط تُمرَّر إلى تلك الدالة.
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
تنتهي العملية بمجرد أن تُكمل دالتها التنفيذ.
يمكنك التحقق مما إذا كانت العملية ما زالت حيّة (أي ما زالت تُنفَّذ) باستخدام Process.alive?/1:
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
لا تتبادل العمليات المعلومات مع بعضها مباشرةً. بل تُرسل العمليات رسائل لمشاركة البيانات. يُسمى نمط التزامن هذا نموذج الفاعل.
أرسل رسائل إلى عملية باستخدام send/2.
send(pid, :hello)
send مما إذا كانت الرسالة قد استُلمت ولا مما إذا كان المستلم ما زال حيًّا.يمكن أن تكون الرسالة من أي نوع.
يمكنك استقبال رسالة أُرسلت إلى العملية الحالية باستخدام receive/1.
receive حتى تصل إلى صندوق بريد العملية رسالة تطابق أيًّا من الأنماط المعطاة.
after._ في receive/1 لتجنب نفاد الذاكرة بسبب تراكم الرسائل غير المقروءة.receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
إذا أردت استقبال أكثر من رسالة واحدة، فأنت بحاجة إلى استدعاء receive/1 بشكل عودي. من الأنماط الشائعة تنفيذ دالة عودية، تُسمّى مثلًا loop، تستدعي receive/1، وتفعل شيئًا بالرسالة، ثم تستدعي نفسها لانتظار المزيد من الرسائل. وإذا احتجت إلى نقل بعض الحالة من استدعاء receive/1 إلى آخر، فيمكنك فعل ذلك بتمرير وسيط إلى دالة 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
من الناحية العملية، نادرًا ما يُستخدم هذا الأسلوب مباشرةً. يقدّم Elixir تجريدات للتزامن، مثل وحدة Agent أو سلوك GenServer، وكلاهما مبني على حلقة الاستقبال. ومع ذلك، من الضروري فهم هذه الأساسيات لتتمكن من استخدام هذه التجريدات بكفاءة.