Web Worker
Web Worker nima, u main thread'dan qanday mustaqil ishlaydi, Structured Clone Algorithm orqali ma'lumot uzatish, Transferable Objects bilan katta buferlarni nusxasiz ko'chirish va Dedicated/Shared/Service Worker turlari farqi.
83-dars
Web Worker
JavaScript — single-threaded til. Bitta tab ichida bitta main thread bor, va shu bitta thread:
- DOM'ni chizadi (render)
- Click, scroll kabi hodisalarni ushlaydi
- Sizning JS kodingizni bajaradi
Bularning barchasi bitta navbatda (call stack + event loop) ketma-ket ishlaydi. Agar biror funksiya uzoq vaqt band qilsa (masalan, 500ms davomida katta massivni qayta ishlash), shu vaqt davomida:
// Main thread'da og'ir hisob
function heavyCalculation() {
let result = 0;
for (let i = 0; i < 5_000_000_000; i++) {
result += Math.sqrt(i);
}
return result;
}
heavyCalculation(); // sahifa 5-10 soniya "muzlab" qoladi
Bu paytda foydalanuvchi tugma bosolmaydi, scroll qilolmaydi, animatsiyalar to'xtaydi — chunki render ham, event handling ham, JS ham bitta thread'da navbatda turibdi.
Web Worker — shu muammoni hal qiladi: u sizga alohida, haqiqiy OS-darajasidagi thread beradi, unda JS kodi main thread'dan mustaqil, parallel ishlaydi.
1. Asosiy qoida — Worker nima qila oladi, nima qila olmaydi
Bu eng ko'p chalkashtiradigan joy:
| Imkoniyat | Worker ichida bormi? |
|---|---|
DOM (document, window) |
❌ Yo'q |
console.log |
✅ Bor |
| Oddiy hisob-kitob, sikllar | ✅ Bor |
fetch, XMLHttpRequest |
✅ Bor |
setTimeout, setInterval |
✅ Bor |
localStorage |
❌ Yo'q (lekin IndexedDB — bor) |
| Boshqa Worker yaratish | ✅ Bor (nested worker) |
Nega DOM yo'q? Chunki DOM — thread-safe emas. Agar ikki thread bir vaqtda DOM'ga tegsa, race condition va korruptsiya bo'lardi. Shuning uchun brauzer arxitektorlari qat'iy qaror qilgan: faqat main thread DOM'ga tega oladi.
2. Amaliyot — asosiy sintaksis
main.js (asosiy fayl):
// Worker'ni yaratamiz — alohida faylni ko'rsatamiz
const worker = new Worker('worker.js');
// Worker'ga xabar (data) yuboramiz
worker.postMessage({ command: 'start', numbers: [1, 2, 3, 4, 5] });
// Worker'dan javob kelganda
worker.onmessage = function (event) {
console.log('Worker javobi:', event.data);
};
// Xatolik bo'lsa
worker.onerror = function (error) {
console.error('Worker xatosi:', error.message);
};
worker.js (alohida fayl — Worker shu faylni bajaradi):
// Worker ichida "self" — bu shu Worker'ning global obyekti (window o'rniga)
self.onmessage = function (event) {
const { command, numbers } = event.data;
if (command === 'start') {
const sum = numbers.reduce((acc, n) => acc + n, 0);
// natijani main thread'ga qaytarib yuboramiz
self.postMessage({ result: sum });
}
};
Diqqat: main.js va worker.js — bu ikkita alohida global scope, ikkita alohida thread. Ular bir-birining o'zgaruvchilariga to'g'ridan-to'g'ri kira olmaydi — faqat postMessage orqali xabar almashadi.
3. Chuqurroq — ma'lumot qanday uzatiladi (Structured Clone Algorithm)
Bu yerda eng muhim texnik detal: worker.postMessage(data) chaqirilganda, data referensiya orqali emas, balki nusxa (copy) sifatida uzatiladi.
const obj = { count: 0 };
worker.postMessage(obj);
obj.count = 100; // Worker ichidagi nusxaga TA'SIR QILMAYDI
Buning mexanizmi — Structured Clone Algorithm:
- Main thread'dagi
postMessage(data)chaqirilganda, brauzer engine datani chuqur (deep) klonlaydi — obyektlar, massivlar, Map, Set, Date — bularning barchasi rekursiv nusxalanadi. - Bu klon serializatsiya qilinadi (ichki, binary formatga).
- Klon Worker thread'iga jismonan ko'chiriladi (memory'da nusxa yaratiladi).
- Worker tarafda klon deserializatsiya qilinib,
event.datasifatida beriladi.
Muhim cheklov: JSON.stringifydan farqli o'laroq, Structured Clone functionlarni, class metodlarini (prototip zanjirini), DOM nodelarni klonlay olmaydi — bular uzatilsa, xato tashlaydi:
worker.postMessage({ fn: () => {} });
// DataCloneError: could not be cloned
Lekin Date, RegExp, Map, Set, ArrayBuffer, Blob kabi murakkab turlarni JSON'dan farqli, to'g'ridan-to'g'ri klonlay oladi — bu Structured Clone'ning JSON.stringifydan ustunligi.
4. Performance muammosi — katta data uzatishda klonlash narxi
Agar siz 100MB'lik ArrayBuffer (masalan, video freym yoki katta rasm ma'lumoti) uzatsangiz, har safar to'liq nusxa yaratiladi — bu ham vaqt, ham xotira jihatidan qimmat:
const bigBuffer = new ArrayBuffer(100 * 1024 * 1024); // 100MB
worker.postMessage(bigBuffer); // 100MB NUSXALANADI — sekin
Buning yechimi — Transferable Objects:
worker.postMessage(bigBuffer, [bigBuffer]);
// ikkinchi argument — "transfer list"
Bu yerda nima bo'ladi: bigBuffer nusxalanmaydi, balki uning egaligi (ownership) main thread'dan Worker'ga jismonan ko'chiriladi (memory pointer almashtiriladi, real data ko'chmaydi — bu O(1) operatsiya). Natijada:
console.log(bigBuffer.byteLength); // 0 — main thread'da endi bo'sh!
Main thread endi shu bufferga kira olmaydi — u butunlay Worker'ga "berib yuborildi".
ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas — shular transferable.
5. Worker turlari — farqi
| Turi | Nima uchun | Umr davomiyligi |
|---|---|---|
| Dedicated Worker | Bitta tab, bitta owner script | Tab yopilsa yoki worker.terminate() chaqirilsa o'ladi |
| Shared Worker | Bir nechta tab/iframe bitta Workerga ulanadi | Barcha ulangan tab'lar yopilguncha yashaydi |
| Service Worker | Tab'dan mustaqil, background proxy (keyingi mavzu) | Tab yopilsa ham fon'da qolishi mumkin |
Shared Worker misoli — ikkita tab bitta hisoblagichni bo'lishishi:
// har ikkala tab'da
const worker = new SharedWorker('shared-worker.js');
worker.port.postMessage('salom');
worker.port.onmessage = (e) => console.log(e.data);
Shared Worker ichida port tushunchasi paydo bo'ladi, chunki bir nechta "mijoz" (tab) bitta Worker'ga ulanadi — har biriga alohida MessagePort ajratiladi.
6. OS darajasida nima sodir bo'ladi
Brauzer Worker yaratganda, u haqiqiy OS thread ochadi. Har bir Worker o'zining:
- V8 (yoki brauzerga qarab boshqa JS engine) instansiyasi
- Alohida heap (xotira)
- Alohida event loop
iga ega. Ya'ni Worker — bu shunchaki "funksiya boshqa joyda ishlaydi" emas, balki butun alohida JS runtime — main thread'ning heap'i, garbage collector'i, call stack'idan butunlay mustaqil.
Shuning uchun ular haqiqatan parallel (multi-core CPU'da bir vaqtda, alohida yadroda) ishlay oladi — bu Node.js'dagi worker_threads moduliga to'g'ridan-to'g'ri mos keladi (aynan shu W3C spec asosida qurilgan).
7. Amaliy qachon ishlatiladi
- Katta massivni sort/filter qilish (masalan, 1 million qatorli jadval)
- Rasm/video processing (piksel darajasida filtr)
- Kriptografik hisoblash (hash, shifrlash)
- PDF generatsiya, katta JSON parse qilish
- Real-time data (WebSocket'dan kelgan katta oqimni qayta ishlash)