مستوى المقال: متوسط. هذا الشرح موجّه لمطوّر JavaScript عنده خبرة سنة على الأقل، فاهم Promises و callbacks، وشاف بعينه واجهة بتتجمّد بدون ما يفهم بالظبط ليه. وقت القراءة المقدّر: 9 دقائق.
لو زرار "Filter" على dashboard بتاعك بياخد 4 ثواني، وخلال الـ 4 ثواني دول المستخدم مش قادر يعمل scroll ولا يدوس على أي حاجة، المشكلة اسمها main thread blocking. Web Workers بـ 8 سطور بيشيلوا المشكلة دي بالكامل وبيخلّوا الواجهة حية حتى لو الحساب لسه شغّال.
Web Workers: ازاي تنفّذ 5 مليون عملية حسابية بدون ما تجمّد المتصفح
المشكلة باختصار
المتصفح بينفّذ JavaScript على thread واحد بس اسمه main thread. نفس الـ thread ده مسؤول عن: تنفيذ الكود، رسم الواجهة (paint)، التعامل مع الكليكات، والـ scroll. لو دالة JavaScript خدت 4 ثواني، الـ 4 الحاجات دول كلهم بيتوقّفوا. ده اللي اسمه "تجمّد الواجهة".
الحل مش "اعمل الكود أسرع" — أحياناً الحساب نفسه لازم ياخد 4 ثواني (تشفير، معالجة صورة، filter على 100 ألف صف). الحل: انقل الحساب لـ thread تاني، وخلّي الـ main thread حر للواجهة.
ابدأ بالمثال — مطعم بطبّاخ واحد
تخيّل مطعم فيه طبّاخ واحد. الزبون الأول طلب طبق يستغرق 4 دقائق. خلال الـ 4 دقائق دول، الطبّاخ مش قادر ياخد طلب جديد، الجرسون مش قادر يقول للزبون رقم 2 "حضرتك تطلب إيه"، باقي الزبائن قاعدين يبصّوا في السقف.
الحل المنطقي: وظّف طبّاخ ثاني في المطبخ الجانبي. الطبّاخ الأصلي يفضّل يستلم طلبات ويردّ على الزبائن، والطبّاخ الجديد يطبخ بهدوء. لمّا الأكل يخلص، يصرخ "جاهز" والطبّاخ الأصلي يوصّله للزبون.
JavaScript في المتصفح بالظبط زي المطعم ده. الطبّاخ الواحد = main thread. الطبّاخ الثاني = Web Worker. التواصل بينهم = postMessage و onmessage.
التعريف العلمي بدقة
طبقاً لتوثيق WHATWG HTML Living Standard (قسم Workers)، الـ Web Worker هو background script بيشتغل في execution context مستقل تماماً عن الـ document context. الـ worker عنده global scope خاص بيه اسمه DedicatedWorkerGlobalScope، وما عندوش وصول لـ DOM ولا window ولا document. التواصل مع main thread بيحصل حصراً عبر postMessage() و onmessage، باستخدام Structured Clone Algorithm لنقل البيانات.
على مستوى الـ runtime: محرّكات JavaScript الحديثة (V8 في Chrome، SpiderMonkey في Firefox، JavaScriptCore في Safari) بتنفّذ كل worker على OS thread منفصل، مع isolate مستقل (heap منفصل + garbage collector مستقل). يعني لو الـ main thread وقع في loop لانهائي، الـ worker لسه بيشتغل، والعكس صحيح.
الكود — مثال يقيس الفرق فعلياً
هنحسب أعداد أوّلية حتى 5 مليون. أول الكود من غير worker: