Seriya: JavaScript Basics Dars 83

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:

  1. Main thread'dagi postMessage(data) chaqirilganda, brauzer engine datani chuqur (deep) klonlaydi — obyektlar, massivlar, Map, Set, Date — bularning barchasi rekursiv nusxalanadi.
  2. Bu klon serializatsiya qilinadi (ichki, binary formatga).
  3. Klon Worker thread'iga jismonan ko'chiriladi (memory'da nusxa yaratiladi).
  4. Worker tarafda klon deserializatsiya qilinib, event.data sifatida 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)