Web Workers: حل مشكلة الـ Single Thread في JavaScript
المشكلة باختصار
JavaScript في المتصفح بتشتغل على thread واحد اسمه الـ main thread. كل حاجة بتحصل عليه: render الـ DOM، event listeners، setTimeout، وحساباتك. لو كتبت loop بيتكرر 10 مليون مرة، المتصفح بيقف عن الاستجابة لحد ما اللوب يخلّص. الزرار مش بيضغط، الـ scroll مش شغّال، والمستخدم بيفتكر إن الصفحة علّقت.
ليه الـ async/await مش بيحل المشكلة دي
ركز هنا: async/await مش بتشتغل على thread تاني. هي بتفكّ الكود بحيث إنه يرجّع التحكم للـ event loop بين الـ I/O operations. لكن لو عندك حساب CPU تقيل (تشفير، ضغط، معالجة صور)، الكود ده هيفضل على الـ main thread حتى لو غلّفته بـ Promise.
الـ trade-off هنا واضح: async/await ممتازة للـ I/O (fetch, fs.readFile)، وWeb Workers لازمة لحسابات الـ CPU.
إزاي Web Worker بيشتغل بالتفاصيل
الـ Web Worker هو ملف JavaScript منفصل بيشتغل على thread تاني. الاتصال بينه وبين الـ main thread بيحصل بـ messages بس — مفيش shared memory افتراضيًا. ده معناه إن البيانات بتتنسخ بينهم (serialization).
- الـ main thread بيعمل
new Worker("worker.js"). - بيبعت بيانات بـ
worker.postMessage(data). - الـ worker بيستقبل البيانات في
self.onmessageويبدأ الشغل. - بيرجّع النتيجة بـ
self.postMessage(result).
مثال تنفيذي: حساب أعداد أولية بدون ما الـ UI يقف
// main.js
const worker = new Worker("prime-worker.js");
document.getElementById("calcBtn").addEventListener("click", () => {
worker.postMessage({ limit: 10_000_000 });
});
worker.onmessage = (e) => {
document.getElementById("result").textContent =
`عدد الأعداد الأولية: ${e.data.count}`;
};
// الـ UI يفضل يستجيب — جرّب اضغط أزرار تانية
// prime-worker.js
self.onmessage = (e) => {
const { limit } = e.data;
let count = 0;
for (let n = 2; n < limit; n++) {
let isPrime = true;
for (let i = 2; i * i <= n; i++) {
if (n % i === 0) { isPrime = false; break; }
}
if (isPrime) count++;
}
self.postMessage({ count });
};