6 يونيو، 2025
لغات البرمجة

🌀 فهم الـ Event Loop في JavaScript: السر وراء الأداء غير المتزامن

إذا كنت تبرمج بلغة JavaScript، فلا بد أنك سمعت عن الـ Event Loop.
لكنه يبدو للبعض كغموض سحري يجعل الكود يعمل بشكل غير متوقع أحيانًا!

في هذه المقالة، سنكشف الستار عن هذا المفهوم الأساسي، ونشرح:

  • ما هو الـ Event Loop؟
  • كيف يعمل؟
  • لماذا هو مهم في تطبيقات الويب؟
  • وكيف يؤثر على الأداء والتفاعلية؟

🧠 لماذا يحتاج JavaScript إلى Event Loop؟

قبل أن ندخل في التفاصيل التقنية، دعنا نفهم التحدي الأساسي:

JavaScript هي لغة أحادية الخيط (Single-threaded).

يعني ذلك أنها لا تستطيع تنفيذ أكثر من مهمة في نفس اللحظة.
ولكن تطبيقات الويب تحتاج إلى:

  • تحميل البيانات من الخادم.
  • الانتظار لرد من API.
  • التعامل مع نقرات المستخدم.
  • تشغيل مؤقتات.

فكيف يمكن تنفيذ كل ذلك دون أن يتجمد المتصفح؟
هنا يأتي دور الـ Event Loop!


🛠️ المكونات الأساسية التي تحرك الحدث

لفهم الـ Event Loop، نحتاج أولًا أن نعرف المكونات التي تلعب دورًا في تشغيل كود JavaScript:

1. Call Stack (مكدس الاستدعاء)

هو المكون الذي ينفذ أوامر الكود واحدًا تلو الآخر.
يشبه كومة أطباق: ما يوضع آخرًا يُزال أولًا.

jsCopyEditfunction greet() {
  console.log("Hello");
}
greet(); // يُضاف إلى الـ Stack ثم يُنفذ ويُزال

2. Web APIs (المتصفح)

عندما يطلب كود JavaScript شيئًا “غير متزامن” (مثل setTimeout أو fetch
لا يتم تنفيذه فورًا في Call Stack، بل يُرسل إلى Web APIs (جزء من المتصفح أو Node.js).

jsCopyEditsetTimeout(() => {
  console.log("بعد 1 ثانية");
}, 1000);

3. Callback Queue (طابور الانتظار)

بعد انتهاء Web API من تنفيذ مهمتها، يتم إرسال الـ Callback (الدالة المراد تنفيذها لاحقًا) إلى طابور الانتظار.

4. Event Loop (حلقة التشغيل)

هو المسؤول عن مراقبة:

  • هل الـ Call Stack فارغ؟
  • هل هناك دوال جاهزة في طابور الانتظار؟

إذا وجد أن الـ Stack فارغ، يسحب أول دالة من الـ Queue ويُضيفها للتنفيذ.


🔁 كيف يعمل Event Loop عمليًا؟ (سلسلة التنفيذ)

دعنا نفهم دورة حياة الكود:

jsCopyEditconsole.log("1");

setTimeout(() => {
  console.log("2");
}, 0);

console.log("3");

ماذا سيُطبع؟

CopyEdit1
3
2

لماذا؟

  1. console.log("1") → يدخل الـ Stack ويُطبع.
  2. setTimeout(...) → يُرسل إلى Web API.
  3. console.log("3") → يُنفذ مباشرة.
  4. بعد انتهاء الكود الأساسي، يجد Event Loop أن الـ Stack فارغ.
  5. يسحب دالة () => console.log("2") من الـ Queue ويُنفذها.

رغم أن المهلة 0ms، يتم تأجيل التنفيذ حتى يفرغ الـ Stack.


⚙️ Microtasks vs Macrotasks: ما الفرق؟

في JavaScript، هناك نوعان من الطوابير:

النوعالأمثلةالتنفيذ
MacrotasksetTimeout, setIntervalبعد كل دورة Event Loop
MicrotaskPromise.then, queueMicrotaskقبل أي Macrotask

مثال توضيحي:

jsCopyEditconsole.log("start");

setTimeout(() => {
  console.log("timeout");
}, 0);

Promise.resolve().then(() => {
  console.log("promise");
});

console.log("end");

النتيجة:

arduinoCopyEditstart
end
promise
timeout

السبب: الـ Promise يُعتبر Microtask، فيُنفذ قبل الـ setTimeout.


🧪 تطبيق عملي مع Promise + Fetch

jsCopyEditconsole.log("طلب البيانات...");

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then((res) => res.json())
  .then((data) => {
    console.log("تم التحميل:", data.title);
  });

console.log("بانتظار الرد...");

ماذا يحدث وراء الكواليس؟

  1. تُرسل طلب fetch إلى Web API.
  2. تستمر JavaScript في تنفيذ باقي الكود.
  3. عند وصول الرد:
    • يتم تحضير الـ callback داخل Microtask queue.
    • ينتظر Event Loop حتى يُفرغ الـ Stack ثم ينفذه.

💥 ماذا يحدث عند تنفيذ كود ضخم؟

jsCopyEditsetTimeout(() => console.log("جاهز!"), 0);

for (let i = 0; i < 1e9; i++) {
  // كود ثقيل
}

console.log("انتهى العمل الثقيل");

رغم أن المهلة 0ms، سيُطبع "جاهز!" بعد انتهاء الـ Loop الثقيل.

السبب: Stack مشغول، لذا الـ Event Loop ينتظر.


⚠️ المشاكل التي قد تسببها Event Loop

  1. تجميد الواجهة (UI Freezing):
    عند تشغيل كود ضخم دون تقسيمه، تتجمد الصفحة لأن الـ Stack لا يُفرغ.
  2. أحداث يتم تجاهلها:
    إذا كانت الطوابير ممتلئة، قد يتم تجاهل بعض الأحداث أو تتأخر استجاباتها.

🛠️ كيف تحسن أداء تطبيقك باستخدام Event Loop؟

✅ استخدم العمليات غير المتزامنة دائمًا

  • استعمل fetch, async/await بدلًا من انتظار الرد يدويًا.

✅ قسّم الكود الثقيل إلى أجزاء صغيرة

  • بدلاً من تنفيذ Loop ثقيل مرة واحدة، استخدم setTimeout أو requestIdleCallback.

✅ تجنّب الكود المتزامن (Synchronous)

  • لا تستخدم alert, prompt, أو document.write بكثرة.

✅ راقب الأداء

  • استخدم أدوات مثل Chrome DevTools → Performance → Event Loop.

🔮 مستقبل Event Loop في JavaScript

مع تطور JavaScript، ظهرت تقنيات جديدة لتوسيع قدرات Event Loop:

  • Web Workers: لتشغيل كود في خيوط منفصلة.
  • Atomics & SharedArrayBuffer: لمشاركة البيانات بين الخيوط.
  • Event Loop في Deno: نسخة محسنة وأكثر أمانًا.

📌 ملخص سريع

المفهومالتفسير
Call Stackمكان تنفيذ الدوال بشكل متسلسل
Web APIينفذ المهام غير المتزامنة مثل fetch و setTimeout
Callback Queueينتظر فيه الكود حتى يسمح الـ Event Loop بتنفيذه
Event Loopالمراقب الذي يدير متى يُنفذ كل شيء
Microtaskمثل Promise.then وتُنفذ دائمًا قبل Macrotasks

💬 في النهاية

الـ Event Loop ليس مجرد “تفصيلة تقنية”، بل هو قلب JavaScript النابض.

كلما فهمت طريقة عمله بعمق، أصبحت أكثر قدرة على:

  • كتابة كود متجاوب وسلس.
  • اكتشاف الأخطاء الغامضة.
  • بناء تطبيقات أكثر احترافية.

Leave feedback about this