JavaScript Web Workers: Paralel İşlem Rehberi

JavaScript Web Workers: Paralel İşlem Rehberi
JavaScript tek iş parçacıklı (single-threaded) çalışır ve ağır hesaplamalar ana thread'i bloklar, kullanıcı arayüzünü donuklaştırır. Web Workers, arka plan thread'lerinde JavaScript çalıştırarak ana thread'i serbest bırakır. Bu rehberde Dedicated Workers, Shared Workers, mesaj iletişimi, veri transfer yöntemleri ve performans optimizasyonlarını inceleyeceğiz.
Web Worker Türleri
- Dedicated Worker: Tek bir script tarafından kullanılır. En yaygın kullanılan türdür
- Shared Worker: Birden fazla script/sekme tarafından paylaşılabilir
- Service Worker: Ağ isteklerini yakalayan proxy worker (ayrı bir konu)
Dedicated Worker Oluşturma
// main.js (Ana thread)
const worker = new Worker('worker.js');
// Worker'a mesaj gönder
worker.postMessage({
type: 'CALCULATE',
data: { numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
});
// Worker'dan mesaj al
worker.onmessage = (event) => {
console.log('Sonuç:', event.data);
};
// Hata yakalama
worker.onerror = (error) => {
console.error('Worker hatası:', error.message);
};
// Worker'ı sonlandır
// worker.terminate();
// worker.js (Worker thread)
self.onmessage = (event) => {
const { type, data } = event.data;
switch (type) {
case 'CALCULATE': {
// Ağır hesaplama (ana thread'i bloklamaz)
const result = data.numbers.reduce((sum, n) => {
// Simüle edilmiş ağır işlem
let val = n;
for (let i = 0; i < 1000000; i++) {
val = Math.sqrt(val * val + i);
}
return sum + val;
}, 0);
self.postMessage({ type: 'RESULT', result });
break;
}
}
};
Inline Worker (Blob URL)
Ayrı dosya oluşturmadan worker tanımlamak için Blob URL kullanabilirsiniz:
function createInlineWorker(fn) {
const blob = new Blob(
[`self.onmessage = ${fn.toString()}`],
{ type: 'application/javascript' }
);
const url = URL.createObjectURL(blob);
const worker = new Worker(url);
// Cleanup
worker.addEventListener('close', () => URL.revokeObjectURL(url));
return worker;
}
const sortWorker = createInlineWorker((event) => {
const { array } = event.data;
const sorted = array.sort((a, b) => a - b);
self.postMessage({ sorted });
});
sortWorker.postMessage({
array: Array.from({ length: 1000000 }, () => Math.random()),
});
sortWorker.onmessage = (event) => {
console.log('Sıralama tamamlandı:', event.data.sorted.length, 'eleman');
};
Transferable Objects
Büyük verileri kopyalamak yerine sahipliği transfer etmek çok daha performanslıdır:
// Kötü: Veri kopyalanır (yavaş, çift bellek kullanımı)
const largeArray = new Float64Array(1000000);
worker.postMessage({ data: largeArray });
// largeArray hala ana thread'de kullanılabilir
// İyi: Veri transfer edilir (hızlı, sıfır kopya)
const buffer = new Float64Array(1000000);
worker.postMessage({ data: buffer }, [buffer.buffer]);
// buffer artık ana thread'de kullanılamaz (boş)
console.log(buffer.byteLength); // 0
// Worker tarafında
self.onmessage = (event) => {
const data = event.data.data; // Float64Array, transfer edilmiş
// İşlem yap
for (let i = 0; i < data.length; i++) {
data[i] = Math.sqrt(data[i]);
}
// Sonucu geri transfer et
self.postMessage({ result: data }, [data.buffer]);
};
SharedArrayBuffer ile Paylaşımlı Bellek
// Ana thread
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
// Worker'a paylaşımlı bellek gönder
worker.postMessage({ buffer: sharedBuffer });
// Atomics ile thread-safe okuma/yazma
Atomics.store(sharedArray, 0, 42);
console.log(Atomics.load(sharedArray, 0)); // 42
// Worker tarafında
self.onmessage = (event) => {
const sharedArray = new Int32Array(event.data.buffer);
// Atomics ile güvenli erişim
Atomics.add(sharedArray, 0, 10);
const value = Atomics.load(sharedArray, 0); // 52
// Bekleme ve bildirim
Atomics.wait(sharedArray, 1, 0); // index 1 = 0 olduğu sürece bekle
// Ana thread'den Atomics.notify(sharedArray, 1) çağrılınca devam eder
};
SharedArrayBuffer kullanmak için sunucu Cross-Origin-Opener-Policy: same-origin ve Cross-Origin-Embedder-Policy: require-corp başlıklarını göndermelidir.
Worker Pool Pattern
// WorkerPool.js
class WorkerPool {
constructor(workerScript, poolSize = navigator.hardwareConcurrency || 4) {
this.workers = [];
this.queue = [];
this.activeWorkers = new Set();
for (let i = 0; i < poolSize; i++) {
const worker = new Worker(workerScript);
this.workers.push(worker);
}
}
exec(data) {
return new Promise((resolve, reject) => {
const task = { data, resolve, reject };
const availableWorker = this.workers.find(
(w) => !this.activeWorkers.has(w)
);
if (availableWorker) {
this._runTask(availableWorker, task);
} else {
this.queue.push(task);
}
});
}
_runTask(worker, task) {
this.activeWorkers.add(worker);
worker.onmessage = (event) => {
task.resolve(event.data);
this.activeWorkers.delete(worker);
// Kuyrukta bekleyen iş varsa çalıştır
if (this.queue.length > 0) {
const nextTask = this.queue.shift();
this._runTask(worker, nextTask);
}
};
worker.onerror = (error) => {
task.reject(error);
this.activeWorkers.delete(worker);
};
worker.postMessage(task.data);
}
terminate() {
this.workers.forEach((w) => w.terminate());
}
}
// Kullanım
const pool = new WorkerPool('compute-worker.js', 4);
async function processImages(images) {
const results = await Promise.all(
images.map((img) => pool.exec({ type: 'RESIZE', image: img }))
);
return results;
}
Comlink ile Kolay Worker İletişimi
// npm install comlink
// worker.js
import * as Comlink from 'comlink';
const api = {
async fibonacci(n) {
if (n <= 1) return n;
return api.fibonacci(n - 1) + api.fibonacci(n - 2);
},
async processData(items) {
return items.map((item) => ({
...item,
processed: true,
hash: hashFunction(item.data),
}));
},
};
Comlink.expose(api);
// main.js
import * as Comlink from 'comlink';
const worker = new Worker('worker.js');
const api = Comlink.wrap(worker);
// Doğrudan fonksiyon çağrısı gibi kullanılır
const result = await api.fibonacci(40);
console.log('Fibonacci(40):', result);
Web Worker Kullanım Senaryoları
- Resim işleme: Canvas manipülasyonu, filtre uygulama, boyutlandırma
- Veri sıralama/filtreleme: Büyük veri setlerinde arama ve sıralama
- Kriptografi: Hash hesaplama, şifreleme/çözme
- JSON parsing: Büyük JSON dosyalarının parse edilmesi
- Syntax highlighting: Kod editörlerinde sözdizimi renklendirme
- PDF/CSV oluşturma: Dosya üretimi
Web Worker Sınırlamaları
- DOM'a erişemez (
document,windowyok) localStoragevesessionStorageerişimi yok- Ana thread ile iletişim sadece mesajlaşma yoluyla
- Same-origin politikası geçerli
fetch,IndexedDB,WebSocketkullanılabilir
Sonuç
Web Workers, JavaScript'in tek thread sınırlamasını aşmanın ve kullanıcı deneyimini iyileştirmenin güçlü bir yoludur. Ağır hesaplamaları arka plan thread'lerine taşıyarak ana thread'in sorunsuz çalışmasını sağlayın. Transferable Objects ile veri aktarım performansını optimize edin, Worker Pool pattern ile kaynak kullanımını kontrol edin ve Comlink gibi kütüphanelerle geliştirici deneyimini artırın.