مستوى المقال: مبتدئ — لو ليك أقل من سنة في JavaScript أو لسه بتتعلم async/await، المقال ده مكتوب علشانك. لو محترف Node.js، عندك ٧ دقايق هتلاقي فيها مراجعة سريعة + رقم مفاجئ في النهاية.
افتح الكونسول دلوقتي واكتب الكود ده:
setTimeout(() => console.log("setTimeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("Sync");
التوقّع المنطقي إن setTimeout هيظهر قبل Promise لأنه مكتوب قبله في الكود. الواقع: Promise بيظهر قبل setTimeout بـ ٤ مللي ثانية تقريبًا، حتى وانت كاتب صفر. ده مش بَج. ده Event Loop.
Event Loop في JavaScript: ليه Promise بيتنفّذ قبل setTimeout(0)؟
المشكلة باختصار
JavaScript بتشتغل على thread واحد (single-threaded). يعني سطر واحد بس بيتنفّذ في اللحظة. لكن في تطبيق ويب عادي، بتيجيلك ميت حاجة في نفس الوقت: نقرة من المستخدم، رد من السيرفر، Timer خلص، Animation Frame جاي. السؤال: مين بيقرّر يبدأ مين الأول؟ الإجابة: Event Loop. وهو بيستخدم ترتيب أولويات مش واضح من نظرة سريعة على الكود.
مثال للمبتدئ: مطار فيه طابورين
تخيّل مطار فيه بوّابتين للنزول من الطيارة. البوابة الأولى للسفر VIP، والثانية للركاب العاديين. القاعدة بسيطة: ما حدش من الركاب العاديين بينزل قبل ما طابور VIP بالكامل يفضى. حتى لو راكب VIP وصل متأخّر والعاديين كانوا واقفين قبله بساعة، VIP بينزل الأول.
JavaScript بتعمل نفس الكلام بالظبط. Promise هو راكب VIP، setTimeout راكب عادي. حتى لو setTimeout مكتوب أولًا في الكود، Promise بيتقدّم في الطابور.
التعريف العلمي: ٤ مكوّنات هي كل القصة
- Call Stack: المكان اللي الكود الحالي بيتنفّذ فيه. كل سطر sync بيدخله ويطلع منه فورًا.
- Web APIs (أو Node APIs): مش جزء من JavaScript نفسها. هي اللي بتشغّل setTimeout والـ HTTP requests والـ DOM events. لما تنادي setTimeout، الـ JavaScript بتسلّم المهمة دي للمتصفح وتروح تكمل.
- Macrotask Queue (الطابور العادي): لما الـ Web API يخلص شغله — مثلًا Timer انتهى — بيحط الـ callback في الطابور ده. مصادره:
setTimeout,setInterval,I/O,UI events. - Microtask Queue (طابور VIP): مخصّص لـ Promise callbacks (
.then,.catch,.finally) وqueueMicrotaskوMutationObserver.
Event Loop دورته بسيطة: شيل أول مهمّة من Macrotask Queue ونفّذها. بعدها قبل ما تاخد المهمّة التالية من Macrotask Queue. ده الفرق الجوهري: Microtasks بتتنفّذ كلها قبل أي macrotask جديدة.