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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر

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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر

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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر

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

Event Loop في JavaScript: الفهم الذي يغير طريقة برمجتك للأبد

عندما تعلق واجهة المستخدم أو يتجمد السيرفر، فالسبب غالباً هو جهل عميق بكيفية عمل Event Loop. هذا المقال يكشف لك التفاصيل الخفية خلف الكواليس، بالأكواد الحقيقية والأخطاء التي يقع فيها حتى المحترفون.

فريق نوفيل١٦ يوليو ٢٠٢٦7 دقائق قراءة٦٤ مشاهدة

في أحد المشاريع الكبيرة لشركة تقنية مشهورة، كان السيرفر يتجمد تماماً كل يوم في ساعة الذروة. المطورون فحصوا كل شيء: قواعد البيانات، الـ Network Latency، حتى الـ Memory Leaks. لكن المشكلة كانت في سطر واحد من الكود: حلقة while لا تنتهي أبداً داخل معالج حدث. هذا السطر لم يكن خطأً منطقياً واضحاً، بل كان انتهاكاً غير مقصود لقواعد Event Loop في JavaScript. النتيجة؟ الـ Call Stack امتلأ، الـ Event Loop علق، والمستخدمون رأوا صفحة بيضاء لمدة ٣٠ ثانية كاملة. هذا ليس مجرد بطء، بل هو فشل كامل في تجربة المستخدم.

الحقيقة المؤلمة هي أن معظم المطورين يستخدمون مصطلحات مثل async/await وPromises دون أن يفهموا حقاً كيف تعمل خلف الكواليس. يقولون "JavaScript هي لغة single-threaded" لكنهم لا يعرفون ماذا يعني ذلك عملياً. عندما تكتب setTimeout أو fetch، هل تعلم أن هذه العمليات لا تُنفذ في الوقت الذي تتوقع؟ وأن الـ Event Loop هو الذي يقرر متى وأين ستظهر نتائجها؟ هذا المقال ليس مجرد شرح نظري، بل هو رحلة إلى أعماق محرك V8، حيث ستفهم كيف تُدار المهام، وكيف تُعالج الأخطاء، وكيف يمكنك كتابة كود لا يعلق أبداً.

الأسطورة الخاطئة: JavaScript تعمل على thread واحد فقط

نعم، JavaScript هي لغة single-threaded من حيث تنفيذ الكود، لكن هذا لا يعني أنها تعمل على thread واحد فقط. المحرك (مثل V8 في Chrome أو SpiderMonkey في Firefox) يستخدم عدة threads خلف الكواليس لإدارة العمليات الثقيلة مثل الـ I/O، الـ Timers، وحتى الـ Garbage Collection. الـ Event Loop هو الجسر الذي يربط بين هذه الـ threads وبين الـ Call Stack الذي ينفذ كودك. عندما تسمع أن JavaScript هي single-threaded، فهذا يعني أن الكود الذي تكتبه يُنفذ على thread واحد فقط، لكن العمليات الأخرى (مثل قراءة ملف أو إرسال طلب HTTP) تُنفذ على threads أخرى في الخلفية.

المشكلة تبدأ عندما يظن المطور أن كل شيء يعمل على نفس الـ thread. مثلاً، عندما تكتب كوداً مثل هذا:

javascript
// مثال خاطئ: الاعتقاد أن هذا الكود يعمل على نفس الـ thread
function processData() {
 const data = fs.readFileSync('large-file.json', 'utf8'); // blocking I/O
 return JSON.parse(data);
}

// هذا الكود يعلق الـ Event Loop بالكامل!
while (true) {
 const result = processData();
 console.log(result);
}

في هذا المثال، الـ readFileSync هو عملية I/O blocking، مما يعني أن الـ Event Loop لن يستطيع معالجة أي أحداث أخرى حتى تنتهي هذه العملية. النتيجة؟ واجهة المستخدم تتجمد، السيرفر لا يستجيب، والمستخدمون يهربون. الحل؟ استخدام الـ async APIs مثل readFile بدلاً من readFileSync، لكن حتى هذا ليس كافياً إذا لم تفهم كيف يدير الـ Event Loop هذه العمليات.

تشريح الـ Event Loop: كيف تُدار المهام خلف الكواليس

الـ Event Loop هو حلقة لا نهائية (loop) تتحقق باستمرار مما إذا كان هناك مهام جاهزة للتنفيذ في الـ Call Stack. لكنها لا تتحقق عشوائياً، بل تتبع ترتيباً دقيقاً يعتمد على نوع المهمة. هناك عدة مراحل في دورة الـ Event Loop، وكل مرحلة لها دور محدد:

  • •**المرحلة الأولى: Timers** - هنا تُنفذ الـ callbacks الخاصة بالـ setTimeout وsetInterval. لكن انتبه: إذا كان الـ Call Stack مشغولاً، فلن تُنفذ هذه الـ callbacks حتى يفرغ.
  • •**المرحلة الثانية: Pending Callbacks** - تُنفذ الـ callbacks الخاصة بالعمليات التي انتهت في الخلفية، مثل الـ I/O أو الـ Network Requests.
  • •**المرحلة الثالثة: Idle, Prepare** - مرحلة داخلية تُستخدم لتحضير العمليات القادمة.
  • •**المرحلة الرابعة: Poll** - هنا تُستقبل الأحداث الجديدة (مثل طلبات HTTP الجديدة أو أحداث الـ DOM). إذا لم يكن هناك أحداث، قد ينتظر الـ Event Loop لفترة قصيرة قبل الانتقال للمرحلة التالية.
  • •**المرحلة الخامسة: Check** - تُنفذ الـ callbacks الخاصة بالـ setImmediate (في Node.js).
  • •**المرحلة السادسة: Close Callbacks** - تُنفذ الـ callbacks الخاصة بالأحداث المغلقة، مثل الـ sockets أو الـ streams.

لكن كيف يتفاعل هذا مع الكود الذي تكتبه؟ لنأخذ مثالاً عملياً:

javascript
console.log('Start');

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

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

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

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

console.log('End');

ماذا تتوقع أن يكون ترتيب الطباعة؟ إذا قلت "Start, End, Timeout 1, Timeout 2, Promise 1, Promise 2" فأنت مخطئ. النتيجة الفعلية هي:

text
Start
End
Promise 1
Promise 2
Timeout 1
Timeout 2

لماذا؟ لأن الـ Promises تُنفذ في مرحلة مختلفة عن الـ Timers. الـ Promises تُضاف إلى الـ Microtask Queue، التي تُنفذ بعد الـ Call Stack الحالي وقبل الـ Macrotask Queue (التي تحتوي على الـ setTimeout). هذا يعني أن الـ Microtasks لها أولوية أعلى من الـ Macrotasks، وهذا هو السبب في أن الـ Promises تُنفذ قبل الـ setTimeout حتى لو كان وقت الـ setTimeout هو ٠.

الـ Microtasks vs. الـ Macrotasks: الفرق الذي يحدد أداء تطبيقك

الفرق بين الـ Microtasks والـ Macrotasks هو أحد أكثر المفاهيم التي تُفهم خطأً في JavaScript. الـ Microtasks هي مهام صغيرة تُنفذ فوراً بعد الـ Call Stack الحالي وقبل أي شيء آخر في دورة الـ Event Loop. من أمثلتها:

  • •الـ callbacks الخاصة بالـ Promises (مثل .then و .catch)
  • •الـ queueMicrotask API
  • •الـ MutationObserver في المتصفح

أما الـ Macrotasks فهي مهام أكبر تُنفذ في المراحل المختلفة من دورة الـ Event Loop. من أمثلتها:

  • •الـ setTimeout و setInterval
  • •الـ I/O callbacks (مثل قراءة ملف أو إرسال طلب HTTP)
  • •الـ UI rendering في المتصفح
  • •الـ setImmediate في Node.js

المشكلة تحدث عندما تُضاف الكثير من الـ Microtasks إلى الـ Queue، مما يمنع الـ Event Loop من الانتقال إلى الـ Macrotasks. مثلاً:

javascript
function recursivePromise() {
 Promise.resolve().then(() => {
 console.log('Microtask executed');
 recursivePromise(); // إضافة مهمة جديدة إلى الـ Microtask Queue
 });
}

recursivePromise();

setTimeout(() => {
 console.log('This will never run!');
}, 0);

في هذا المثال، الـ setTimeout لن يُنفذ أبداً لأن الـ Microtask Queue ممتلئ بالمهام التي تُضاف باستمرار. النتيجة؟ الـ Event Loop يعلق، والتطبيق يتجمد. هذا هو السبب في أن بعض المكتبات مثل React تستخدم تقنيات مثل الـ Batched Updates لتقليل عدد الـ Microtasks التي تُضاف إلى الـ Queue.

الأخطاء الشائعة التي تدمر الـ Event Loop (وحلولها)

هناك أخطاء يقع فيها حتى المطورون المحترفون عندما يتعلق الأمر بالـ Event Loop. إليك بعضها، مع حلول عملية:

الخطأ الأول: استخدام الـ while loops في معالجات الأحداث

هذا هو الخطأ الذي ذكرته في بداية المقال. عندما تكتب حلقة while لا تنتهي داخل معالج حدث، فإنك تعلق الـ Event Loop بالكامل. مثلاً:

javascript
button.addEventListener('click', () => {
 while (true) {
 console.log('This will freeze the UI!');
 }
});

الحل؟ استخدم الـ setInterval أو الـ requestAnimationFrame بدلاً من الـ while loops. مثلاً:

javascript
let isRunning = true;

button.addEventListener('click', () => {
 const interval = setInterval(() => {
 if (!isRunning) clearInterval(interval);
 console.log('This will not freeze the UI!');
 }, 100);
});

// لإيقاف الحلقة
stopButton.addEventListener('click', () => {
 isRunning = false;
});

الخطأ الثاني: تجاهل الـ I/O Bound Operations

العمليات التي تعتمد على الـ I/O (مثل قراءة الملفات أو إرسال طلبات HTTP) يمكن أن تكون بطيئة جداً إذا لم تُدار بشكل صحيح. مثلاً، إذا كنت تستخدم الـ readFileSync في Node.js، فإنك تعلق الـ Event Loop بالكامل حتى تنتهي العملية. الحل؟ استخدم الـ async APIs دائماً:

javascript
// خطأ: استخدام الـ readFileSync
const data = fs.readFileSync('file.json', 'utf8');

// صحيح: استخدام الـ readFile
fs.readFile('file.json', 'utf8', (err, data) => {
 if (err) throw err;
 console.log(data);
});

// أو أفضل: استخدام الـ Promises مع fs.promises
async function readFile() {
 const data = await fs.promises.readFile('file.json', 'utf8');
 console.log(data);
}

الخطأ الثالث: إضافة الكثير من الـ Microtasks

كما رأينا سابقاً، إضافة الكثير من الـ Microtasks يمكن أن تعلق الـ Event Loop. هذا يحدث غالباً في المكتبات التي تستخدم الـ Promises بشكل مكثف، مثل مكتبات الـ State Management. الحل؟ استخدم تقنيات مثل الـ Debouncing أو الـ Throttling لتقليل عدد الـ Microtasks التي تُضاف إلى الـ Queue. مثلاً:

javascript
function debounce(fn, delay) {
 let timeoutId;
 return function(...args) {
 clearTimeout(timeoutId);
 timeoutId = setTimeout(() => {
 fn.apply(this, args);
 }, delay);
 };
}

// استخدام الـ debounce لتقليل عدد الـ Microtasks
const debouncedHandler = debounce(() => {
 Promise.resolve().then(() => {
 console.log('This will not flood the Microtask Queue!');
 });
}, 100);

كيف تختبر أداء الـ Event Loop في تطبيقك؟

إذا كنت تريد التأكد من أن الـ Event Loop في تطبيقك يعمل بكفاءة، فهناك عدة أدوات يمكنك استخدامها. في المتصفح، يمكنك استخدام الـ Performance API لقياس وقت تنفيذ المهام:

javascript
const observer = new PerformanceObserver((list) => {
 const entries = list.getEntries();
 entries.forEach((entry) => {
 console.log(`Task took ${entry.duration}ms`);
 });
});

observer.observe({ type: 'longtask', buffered: true });

في Node.js، يمكنك استخدام وحدة الـ perf_hooks لقياس أداء الـ Event Loop:

javascript
const { performance, PerformanceObserver } = require('perf_hooks');

const obs = new PerformanceObserver((items) => {
 items.getEntries().forEach((entry) => {
 console.log(`Event Loop took ${entry.duration}ms`);
 });
});

obs.observe({ entryTypes: ['measure'] });

performance.mark('start');
// الكود الذي تريد قياسه
performance.mark('end');
performance.measure('Event Loop', 'start', 'end');

هذه الأدوات تساعدك على اكتشاف المهام التي تستغرق وقتاً طويلاً وتعيق الـ Event Loop. مثلاً، إذا وجدت أن مهمة معينة تستغرق أكثر من ٥٠ مللي ثانية، فهذا يعني أنها قد تؤثر على تجربة المستخدم ويجب تحسينها.

خلاصة المهندس: نصائح لا تُنسى لتحسين الـ Event Loop

بعد سنوات من العمل مع JavaScript، هذه هي النصائح الذهبية التي أتبعها دائماً لضمان أن الـ Event Loop يعمل بكفاءة:

  • •**تجنب الـ Blocking APIs** - استخدم دائماً الـ async APIs بدلاً من الـ sync APIs، خاصة في العمليات التي تعتمد على الـ I/O.
  • •**قلل من الـ Microtasks** - لا تضف الكثير من الـ Promises أو الـ queueMicrotask إلى الـ Queue، واستخدم تقنيات مثل الـ Debouncing لتقليل الحمل.
  • •**قس أداء الـ Event Loop** - استخدم أدوات مثل الـ Performance API في المتصفح أو وحدة الـ perf_hooks في Node.js لقياس أداء المهام.
  • •**افهم الفرق بين الـ Microtasks والـ Macrotasks** - هذا الفرق هو ما يحدد ترتيب تنفيذ المهام في الـ Event Loop.
  • •**لا تستخدم الـ while loops في معالجات الأحداث** - استخدم الـ setInterval أو الـ requestAnimationFrame بدلاً منها.

الـ Event Loop ليس مجرد مفهوم نظري، بل هو الأساس الذي يبني عليه أداء تطبيقاتك. عندما تفهم كيف يعمل، ستكتب كوداً أسرع وأكثر كفاءة، وستتفادى الأخطاء التي تجعل المستخدمين يهربون من تطبيقك. في المرة القادمة التي تكتب فيها setTimeout أو Promise، تذكر أن هناك عالماً كاملاً خلف الكواليس يدير هذه العمليات، وأن فهم هذا العالم هو ما يميز المطور الجيد عن المطور العظيم.


إذا أعجبك هذا المقال، شاركه مع زملائك المطورين، ودعهم يعرفون أن الـ Event Loop ليس مجرد موضوع نظري، بل هو المفتاح لكتابة تطبيقات سريعة وسلسة. ولا تنسَ أن تجرب الأكواد بنفسك لترى كيف تؤثر التغييرات الصغيرة على أداء الـ Event Loop في تطبيقاتك.

Event Loop JavaScript Async Programming Node.js Web Performance

التعليقات

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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر