نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/JavaScript
JavaScript

Event Loop في JavaScript: الفهم الذي يعيد تشكيل طريقة كتابتك للكود

كيف يعمل الـ Event Loop حقاً خلف الكواليس؟ اكتشف الآليات الخفية التي تجعل JavaScript غير متزامنة، وكيف تفادي الأخطاء الشائعة التي تكلف الشركات ساعات تصحيح في الإنتاج.

فريق نوفيل٢٠ أغسطس ٢٠٢٦7 دقائق قراءة٨ مشاهدة

في أحد الأيام الصعبة في شركة ناشئة كنت أعمل فيها، علق سيرفر الـ Node.js بشكل غامض تحت ضغط طلبات متواضع: ٢٠٠ طلب في الثانية. الـ CPU كان عند ١٠٪، والذاكرة مستقرة، لكن الـ API كان يستغرق ٣٠ ثانية للرد بدلاً من ٣٠٠ مللي ثانية. المشكلة؟ لم تكن في قاعدة البيانات ولا في الـ Network Latency، بل في سطر واحد من الكود استخدم فيه زميل مبتدئ setTimeout مع ٠ ميلي ثانية ظناً منه أنه سيجعل التنفيذ أسرع. هذا السطر البسيط قلب الـ Event Loop رأساً على عقب، وأظهر لي أن الفهم السطحي لهذه الآلية هو أسوأ من الجهل بها تماماً.

الـ Event Loop ليس مجرد مفهوم نظري تدرسه في كورس JavaScript ثم تنساه. إنه العمود الفقري الذي يجعل لغة تعمل على خيط واحد (single-threaded) قادرة على التعامل مع آلاف العمليات غير المتزامنة دون أن تعلق. لكن هنا تكمن المفارقة: معظم المطورين يعتقدون أنهم يفهمونه، بينما في الواقع يستخدمونه كصندوق أسود دون أن يعرفوا ماذا يحدث داخله. والنتيجة؟ كود يبدو صحيحاً لكنه يتصرف بغرابة تحت الضغط، أو تطبيقات تتجمد دون سبب واضح، أو أسوأ: تسريبات ذاكرة لا تُكتشف إلا بعد ساعات من التشغيل في الإنتاج.

ما هو الـ Event Loop حقاً؟ ليس مجرد حلقة، بل نظام متكامل

عندما تسمع كلمة "loop" قد تظن أنها مجرد حلقة تكرارية بسيطة مثل for أو while. لكن الـ Event Loop في JavaScript هو نظام معقد يتكون من عدة مراحل ومكونات تعمل بتناغم دقيق. فكر فيه كمدير عمليات ذكي داخل محرك JavaScript (V8 في حالة Node.js وChrome) مسؤول عن جدولة وتنفيذ المهام بطريقة تضمن عدم حظر الخيط الرئيسي. هذا النظام يتكون من عدة قوائم انتظار (queues) ومكدسات (stacks) تعمل معاً بتنسيق دقيق: Call Stack، Microtask Queue، Macrotask Queue، وRender Queue في المتصفح.

الخطأ الشائع هو الاعتقاد أن الـ Event Loop يعمل بطريقة خطية بسيطة: يأخذ مهمة من Queue وينفذها ثم يأخذ المهمة التالية. الحقيقة أكثر تعقيداً بكثير. مثلاً، الـ Microtask Queue له الأولوية المطلقة على الـ Macrotask Queue، وهذا يعني أن Promise.then() سيتم تنفيذه قبل setTimeout حتى لو كان الـ setTimeout مجدولاً قبله. هذه الأولوية ليست مجرد تفصيل صغير، بل هي ما يجعل مكتبات مثل React قادرة على تحديث واجهة المستخدم بسرعة دون تأخير ملحوظ. في مشروع سابق، استخدمنا هذه الخاصية لتنفيذ عمليات تحديث قاعدة البيانات في الخلفية دون أن يؤثر ذلك على استجابة واجهة المستخدم، ببساطة عن طريق تغليف العمليات في Promises ووضعها في الـ Microtask Queue بدلاً من الـ Macrotask Queue.

javascript
// مثال يوضح أولوية Microtask Queue على Macrotask Queue
console.log('Start');

setTimeout(() => {
 console.log('Timeout');
}, 0);

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

console.log('End');
// الناتج:
// Start
// End
// Promise
// Timeout

// لاحظ أن Promise.then() تم تنفيذه قبل setTimeout رغم أن setTimeout مجدول أولاً
// وهذا لأن الـ Microtask Queue له أولوية أعلى من الـ Macrotask Queue

المراحل الداخلية للـ Event Loop: رحلة المهمة من الجدولة إلى التنفيذ

الـ Event Loop لا يعمل بطريقة عشوائية، بل يمر بعدة مراحل محددة بدقة في كل دورة (tick). هذه المراحل هي: Timers، Pending Callbacks، Idle/Prepare، Poll، Check، وClose Callbacks. كل مرحلة لها دور محدد ومسؤوليات معينة، وفهم هذه المراحل هو ما يميز المطور الذي يفهم كيف يعمل الكود حقاً عن الذي يكتب كوداً يعمل بالصدفة فقط.

لنأخذ مرحلة الـ Poll كمثال. هذه المرحلة هي المسؤولة عن جلب الأحداث الجديدة من نظام التشغيل، مثل عمليات الإدخال/الإخراج (I/O) المكتملة أو أحداث الشبكة. لكن هنا تكمن المفاجأة: الـ Event Loop لا ينتظر في هذه المرحلة حتى تكتمل جميع عمليات I/O، بل ينتظر فقط لفترة محددة (يمكن أن تكون صفراً) ثم ينتقل إلى المرحلة التالية. هذا السلوك هو ما يجعل Node.js قادراً على التعامل مع آلاف الاتصالات المتزامنة دون الحاجة إلى خيوط متعددة. في أحد المشاريع الكبيرة، استخدمنا هذه الخاصية لبناء نظام بث مباشر يتعامل مع ٥٠ ألف مشاهد متزامن باستخدام خادم واحد فقط، ببساطة عن طريق تجنب العمليات التي تحجز الـ Event Loop في مرحلة الـ Poll لفترات طويلة.

javascript
// مثال يوضح كيف يمكن أن تعلق مرحلة الـ Poll
// هذا الكود سيجعل الـ Event Loop ينتظر في مرحلة الـ Poll
// مما يمنع تنفيذ أي مهام أخرى حتى تكتمل العملية

const fs = require('fs');

// هذه العملية ستحجز الـ Event Loop في مرحلة الـ Poll
// لأنها عملية I/O مكثفة ولا تستخدم الـ Non-blocking API
const data = fs.readFileSync('large-file.txt', 'utf8');
console.log('File read');

// أي حدث آخر (مثل setTimeout أو Promise) لن يتم تنفيذه
// حتى تكتمل قراءة الملف
setTimeout(() => {
 console.log('Timeout');
}, 0);

// الحل الصحيح هو استخدام الـ Non-blocking API
fs.readFile('large-file.txt', 'utf8', (err, data) => {
 console.log('File read asynchronously');
});

// الآن الـ Event Loop حر في تنفيذ مهام أخرى أثناء انتظار قراءة الملف

كيف تتجنب حجز الـ Event Loop: قواعد ذهبية للمطور المحترف

  • •استخدم دائماً الـ Non-blocking APIs لعملية I/O مثل fs.readFile بدلاً من fs.readFileSync.
  • •تجنب العمليات الحسابية المكثفة في الخيط الرئيسي. إذا اضطررت لذلك، استخدم Web Workers أو قم بتقسيم المهمة إلى أجزاء صغيرة باستخدام setImmediate أو process.nextTick.
  • •لا تستخدم setTimeout(fn, 0) كحل سحري لجعل الكود غير متزامن. استخدم بدلاً منه process.nextTick في Node.js أو queueMicrotask في المتصفح.
  • •كن حذراً مع الـ while loops التي تعتمد على حالة قد لا تتغير بسرعة، مثل الانتظار على متغير يتم تحديثه في callback. هذه الحلقات يمكن أن تحجز الـ Event Loop تماماً.
  • •استخدم أدوات مثل clinic.js أو Chrome DevTools لتحليل أداء الـ Event Loop وتحديد النقاط التي تحجزه.

الـ Event Loop في بيئات مختلفة: المتصفح مقابل Node.js

على الرغم من أن الـ Event Loop هو مفهوم أساسي في JavaScript، إلا أن تطبيقه يختلف قليلاً بين بيئات التشغيل المختلفة. في المتصفح، الـ Event Loop يتعامل مع مهام إضافية مثل تحديث واجهة المستخدم (UI Rendering) والتي تحدث عادةً بتردد ٦٠ مرة في الثانية (٦٠ فريم في الثانية). هذا يعني أن أي مهمة تستغرق أكثر من ١٦ مللي ثانية (١٠٠٠/٦٠) ستؤدي إلى تأخير في تحديث الواجهة، مما يسبب تجربة مستخدم سيئة. لهذا السبب، مكتبات مثل React تستخدم تقنيات مثل Virtual DOM لتقليل كمية العمل التي يحتاجها الـ Event Loop في كل دورة.

في Node.js، الـ Event Loop أكثر تركيزاً على عمليات I/O، حيث أن Node مصمم للتعامل مع عدد كبير من الاتصالات المتزامنة. لكن هذا التركيز يأتي بتحدياته الخاصة. مثلاً، في المتصفح، يمكنك الاعتماد على أن الـ UI Render سيحدث بانتظام، بينما في Node.js، إذا حجزت الـ Event Loop بعملية طويلة، فلن يكون هناك أي تحديثات حتى تكتمل العملية. في أحد المشاريع، واجهنا هذه المشكلة عندما استخدمنا مكتبة معالجة صور ثقيلة في الـ Backend. الحل كان نقل هذه العملية إلى خدمة منفصلة تعمل على خادم آخر، مما سمح للـ Event Loop في الخادم الرئيسي بالبقاء حراً للتعامل مع طلبات المستخدمين.

javascript
// مثال يوضح الفرق بين المتصفح وNode.js في التعامل مع الـ Event Loop

// في المتصفح:
// هذه المهمة ستؤخر تحديث واجهة المستخدم إذا استغرقت أكثر من 16ms
function heavyComputation() {
 let sum = 0;
 for (let i = 0; i < 1e8; i++) {
 sum += i;
 }
 return sum;
}

// الحل في المتصفح: تقسيم المهمة باستخدام setTimeout
function chunkedComputation() {
 let sum = 0;
 function chunk(i) {
 for (let j = 0; j < 1e6; j++) {
 sum += i++;
 if (i >= 1e8) return console.log(sum);
 }
 setTimeout(() => chunk(i), 0);
 }
 chunk(0);
}

// في Node.js:
// يمكن استخدام setImmediate لتقسيم المهمة
function nodeChunkedComputation() {
 let sum = 0;
 function chunk(i) {
 for (let j = 0; j < 1e6; j++) {
 sum += i++;
 if (i >= 1e8) return console.log(sum);
 }
 setImmediate(() => chunk(i));
 }
 chunk(0);
}

الفخاخ الشائعة وكيف تتجنبها: تجارب من أرض الواقع

خلال مسيرتي المهنية، رأيت العديد من الأخطاء المتعلقة بالـ Event Loop تكلف الشركات الوقت والمال. أحد أكثر الأخطاء شيوعاً هو استخدام setTimeout مع ٠ ميلي ثانية ظناً أن ذلك سيجعل الكود غير متزامن. المشكلة في هذا الأسلوب أنه يضيف المهمة إلى الـ Macrotask Queue، والتي قد تتأخر إذا كان هناك العديد من المهام الأخرى في الـ Queue. في مشروع لشركة تكنولوجيا مالية، تسبب هذا الخطأ في تأخير معالجة طلبات المستخدمين بمقدار ٥ ثوانٍ تحت الضغط، مما أدى إلى خسائر مالية بسبب تأخير تنفيذ الصفقات.

خطأ آخر شائع هو الاعتماد على ترتيب تنفيذ الـ Callbacks في الـ Event Loop دون فهم الأولويات. مثلاً، قد تظن أن الكود التالي سيطبع ١ ثم ٢ ثم ٣، لكنه في الواقع سيطبع ١ ثم ٣ ثم ٢. السبب هو أن process.nextTick يتم تنفيذه قبل الـ I/O Callbacks، حتى لو كان الـ I/O Callback مجدولاً أولاً. هذا السلوك يمكن أن يسبب مشاكل صعبة التصحيح في الأنظمة الكبيرة، خاصة عندما تعتمد على ترتيب تنفيذ العمليات.

javascript
// مثال على خطأ ترتيب التنفيذ في الـ Event Loop
console.log(1);

fs.readFile('file.txt', () => {
 console.log(2);
});

process.nextTick(() => {
 console.log(3);
});

// الناتج: 1 ثم 3 ثم 2
// لأن process.nextTick له أولوية أعلى من الـ I/O Callbacks

كيف تصمم كوداً صديق للـ Event Loop: نصائح من مهندس سنيور

  • •استخدم الـ Worker Threads في Node.js للعمليات الحسابية الثقيلة. هذه الخاصية تسمح لك بتشغيل كود JavaScript على خيوط متعددة دون أن تحجز الـ Event Loop.
  • •قم بتقسيم المهام الطويلة إلى أجزاء صغيرة باستخدام setImmediate أو process.nextTick. هذا يسمح للـ Event Loop بالتعامل مع مهام أخرى بين أجزاء المهمة.
  • •استخدم مكتبات مثل async أو Bluebird للتعامل مع الـ Promises بطريقة أكثر كفاءة. هذه المكتبات توفر أدوات متقدمة للتحكم في تدفق التحكم (control flow) وتقليل الحمل على الـ Event Loop.
  • •راقب أداء الـ Event Loop باستخدام أدوات مثل clinic.js أو Chrome DevTools. هذه الأدوات تساعدك على تحديد النقاط التي تحجز الـ Event Loop وتؤثر على أداء التطبيق.
  • •تجنب استخدام الـ Synchronous APIs في الكود الإنتاجي. حتى لو كانت العملية تبدو بسيطة، يمكن أن تسبب مشاكل تحت الضغط.

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

الـ Event Loop ليس مجرد تفاصيل تنفيذية، بل هو عقل JavaScript. فهمه بعمق سيغير طريقة كتابتك للكود من الأساس. القاعدة الذهبية التي يجب أن تتذكرها دائماً: لا تحجز الـ Event Loop أبداً. أي عملية تستغرق أكثر من بضعة مللي ثانية يجب أن تُقسم أو تُنقل إلى خيط آخر. وعندما تواجه مشكلة أداء غامضة، اسأل نفسك دائماً: هل هذه المشكلة تتعلق بالـ Event Loop؟ في ٩٠٪ من الحالات، ستكون الإجابة نعم.

الخطوة التالية؟ ابدأ بتحليل تطبيقاتك الحالية باستخدام أدوات مثل clinic.js أو Chrome DevTools. ابحث عن العمليات التي تحجز الـ Event Loop وحاول تحسينها. وتذكر: الكود الذي يعمل على جهازك المحلي قد يفشل تحت الضغط في الإنتاج. الـ Event Loop هو ما يحدد الفرق بين التطبيق الذي يعمل والتطبيق الذي يعمل بشكل ممتاز.

Event Loop Node.js JavaScript Advanced Web Performance Asynchronous Programming

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر