حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة
تصوّر كيفية تنفيذ JavaScript: مكدس الاستدعاءات، وطابور المهام، والمهام المصغّرة الناتجة عن Promises، ولماذا لا تتجمّد واجهة المستخدم أثناء العمليات غير المتزامنة.
حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة درس مجاني في Frontend Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Frontend Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Frontend Academy 4 دروس في المجموع.
JavaScript أحادية الخيط
تعمل JavaScript في خيط واحد، أي لا يمكنها تنفيذ أكثر من جزء واحد من الكود في الوقت نفسه. ومع ذلك، تتعامل مع آلاف العمليات غير المتزامنة (طلبات الشبكة والمؤقتات وأحداث واجهة المستخدم) دون حجب التنفيذ. كيف؟ بواسطة حلقة الأحداث.
مكدس الاستدعاءات
مكدس الاستدعاءات هو المكان الذي تتتبع فيه JavaScript الدالة التي تعمل حاليًا. عند استدعاء دالة، يُضاف إطار إلى المكدس. وعند عودتها، يُزال الإطار. وإذا امتلأ المكدس (بسبب التكرار اللانهائي)، يحدث تجاوز للمكدس.
function c() { /* does something */ }
function b() { c(); }
function a() { b(); }
a();
// Call stack during c():
// c ← top
// b
// a
// (anonymous)Web APIs — إسناد العمل
عندما تستدعي JavaScript دالة غير متزامنة (setTimeout أو fetch أو addEventListener)، يُسلَّم العمل إلى Web API في المتصفح. ويواصل محرّك JavaScript تنفيذ السطر التالي، إذ لا ينتظر.
console.log('1');
setTimeout(() => console.log('2'), 0); // handed to Web API
console.log('3');
// Output: 1, 3, 2قائمة المهام (قائمة المهام الكبرى)
عند اكتمال عملية تنفذها Web API (انطلاق المؤقت أو حلّ fetch)، توضع دالة رد الاتصال في قائمة المهام (قائمة المهام الكبرى). لا تنقل حلقة الأحداث دالة رد الاتصال إلى مكدس الاستدعاءات إلا عندما يكون المكدس فارغًا.
قائمة المهام الصغيرة
تستخدم Promises قائمة المهام الصغيرة — وهي قائمة ذات أولوية عالية. بعد اكتمال كل مهمة وقبل اختيار المهمة التالية، تُنفَّذ جميع المهام الصغيرة المعلّقة. تُنفَّذ المهام الصغيرة دائمًا قبل المهمة الكبرى التالية.
console.log('1');
setTimeout(() => console.log('timeout'), 0); // macrotask
Promise.resolve().then(() => console.log('promise')); // microtask
console.log('2');
// Output: 1, 2, promise, timeoutحلقة الأحداث عمليًا
تعمل خوارزمية حلقة الأحداث كما يلي: 1) نفّذ المهمة الحالية من مكدس الاستدعاءات حتى يفرغ. 2) نفّذ جميع المهام الصغيرة الموجودة في القائمة. 3) أجرِ التصيير اختياريًا. 4) اختر المهمة الكبرى التالية وكرّر العملية.
queueMicrotask()
تجدول queueMicrotask(fn) دالة رد اتصال في قائمة المهام الصغيرة من دون إنشاء Promise. نادرًا ما تحتاجون إلى استخدامها مباشرةً، لكنها مفيدة لفهم كيفية تنفيذ المكتبات لآلية الجدولة.
queueMicrotask(() => console.log('microtask'));
console.log('sync');
// Output: sync, microtaskلماذا تؤدي المهام الطويلة إلى تجميد واجهة المستخدم
إذا استغرقت مهمة متزامنة وقتًا طويلًا جدًا (مثل حلقة ضيقة تعالج كمية كبيرة من البيانات)، فلن تتمكن حلقة الأحداث من معالجة أحداث واجهة المستخدم أو عمليات التصيير. يؤدي ذلك إلى تجميد الصفحة. قسّموا العمل الطويل إلى أجزاء باستخدام setTimeout لإعادة التحكم إلى حلقة الأحداث.
// Bad — blocks the UI for seconds:
for (let i = 0; i < 10_000_000; i++) { /* heavy work */ }
// Better — yield periodically:
async function processInChunks(items) {
for (let i = 0; i < items.length; i++) {
process(items[i]);
if (i % 100 === 0) await new Promise(r => setTimeout(r, 0)); // yield
}
}Web Workers — التوازي الحقيقي
استخدموا Web Worker للعمليات التي تستهلك المعالج بكثافة — فهو خيط تنفيذ منفصل له حلقة أحداث خاصة به. يتواصل العمال مع الخيط الرئيسي عبر postMessage. لا توجد ذاكرة مشتركة ولا حالات تسابق.
requestAnimationFrame
تجدول requestAnimationFrame(callback) دالة رد اتصال قبل إعادة الرسم التالية للمتصفح. استخدموها للرسوم المتحركة التي يقودها JavaScript — فهي تتزامن مع معدل تحديث الشاشة (60fps عادةً).
function animate(timestamp) {
// update positions based on timestamp
element.style.transform = `translateX(${timestamp * 0.1 % 400}px)`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);تصوّر حلقة الأحداث
يتيح العرض التفاعلي في latentflip.com/loupe لصق الشفرة ومشاهدة مكدس الاستدعاءات وWeb API وقائمة المهام وحلقة الأحداث بحركة بطيئة. وهو أساسي لبناء فهم عملي لهذه الآلية.
اختبار سريع
بعد حلّ Promise، في أي قائمة تُنفَّذ دالة رد الاتصال الخاصة بـ .then()؟
مراجعة: حلقة الأحداث
JavaScript أحادية الخيط. تُرسَل العمليات غير المتزامنة إلى Web APIs. تعود دوال رد الاتصال عبر قائمة المهام (setTimeout ودوال رد الاتصال الخاصة بـ fetch) أو قائمة المهام الصغيرة (Promises). تُفرغ حلقة الأحداث المهام الصغيرة بعد كل مهمة وقبل التصيير التالي أو المهمة الكبرى التالية. تحجب الشفرة المتزامنة الطويلة كل شيء.
الأسئلة الشائعة
هل درس «حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة» مجاني؟
نعم — نص درس «حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Frontend Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Frontend Academy 4 دروس في المجموع.
ماذا ستتعلم في «حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة»؟
تصوّر كيفية تنفيذ JavaScript: مكدس الاستدعاءات، وطابور المهام، والمهام المصغّرة الناتجة عن Promises، ولماذا لا تتجمّد واجهة المستخدم أثناء العمليات غير المتزامنة. تتمرن على Frontend Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Frontend Academy؟
لا تُشترط خبرة سابقة. Frontend Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Frontend Academy هذا؟
نعم. كل درس في Frontend Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- حلقة الأحداث: مكدس الاستدعاءات وطابور المهام والمهام المصغّرة
- دوال الاستدعاء وجحيم الاستدعاءات
- Promises: then وcatch وfinally وPromise.all
- async/await ومعالجة الأخطاء