Web Worker, bir JavaScript dosyasını sayfanın ana iş parçacığından ayrı bir arka plan iş parçacığında çalıştırmanızı sağlayan tarayıcı API'sidir. new Worker("worker.js") ile oluşturulur, ana sayfayla yalnızca postMessage() ve message olayı üzerinden konuşur ve DOM'a doğrudan erişemez. Uzun süren hesaplamaları worker'a taşıdığınızda kullanıcı arayüzü donmadan tepki vermeye devam eder.
Bu yazıda bir worker'ı oluşturmayı, iki yönlü mesajlaşmayı, type: "module" seçeneğini, verinin structured clone ile nasıl kopyalandığını, transferable nesnelerle sahipliğin nasıl devredildiğini, hata olaylarını ve shared worker farkını MDN belgelerine dayanarak anlatıyoruz. Tarayıcıdan bağımsız kısımları (structured clone ve aktarım) Node.js 24 ile çalıştırıp çıktılarını gösterdik. Tüm Web API konularının haritası için Web API öğrenme rehberine bakabilirsiniz.
Web Worker nedir, neden ayrı bir iş parçacığı?
Bir web sayfasındaki JavaScript normalde tek bir ana iş parçacığında çalışır. Arayüzün çizilmesi, tıklama gibi kullanıcı olaylarının işlenmesi ve sizin kodunuz aynı sırayı paylaşır. Büyük bir JSON dosyasını işlemek, bir görüntünün piksellerini tek tek dolaşmak ya da karmaşık bir hesap yapmak bu sırayı uzun süre işgal ederse sayfa donmuş gibi görünür. Olay döngüsünün bu davranışını JavaScript event loop yazısında ayrıntılı bulabilirsiniz.
Worker, Worker() kurucusuna verdiğiniz adresteki betiği gerçek bir işletim sistemi iş parçacığında çalıştırır. Bu betiğin global bağlamı window değildir: dedicated (adanmış) worker için DedicatedWorkerGlobalScope, shared worker için SharedWorkerGlobalScope nesnesidir. Bu yüzden worker içinde globale ulaşmak için window yerine self kullanılır; window yazmak hataya yol açar.
Worker içinde DOM'u değiştiremezsiniz, window nesnesinin bazı yöntemleri de yoktur. Buna karşın fetch(), XMLHttpRequest (ancak responseXML her zaman null döner), WebSocket, IndexedDB, zamanlayıcılar, structuredClone() ve queueMicrotask() gibi pek çok araç kullanılabilir. MDN'deki "Functions and classes available to workers" listesi bunların tamamını verir; importScripts() ise yalnızca worker'larda bulunan bir fonksiyondur.
İlk worker: oluşturma ve iki yönlü mesajlaşma
MDN'in temel örneği iki sayıyı worker'a gönderip çarpımını geri alır. Önce destek kontrolü yapılır, sonra worker oluşturulur. Kurucuya verilen adres mevcut HTML sayfasının konumuna göre çözülür ve çağıran belgeyle aynı origin'de olmalıdır; aksi hâlde yalnızca blob: ya da data: adresi kabul edilir. Aynı origin kavramını CORS yazısında anlattık.
<label>Birinci sayı <input type="number" id="first"></label>
<label>İkinci sayı <input type="number" id="second"></label>
<p class="result"></p>
<script src="main.js"></script>// main.js
const first = document.querySelector("#first");
const second = document.querySelector("#second");
const result = document.querySelector(".result");
if (window.Worker) {
const myWorker = new Worker("worker.js");
[first, second].forEach((input) => {
input.onchange = () => {
myWorker.postMessage([first.value, second.value]);
};
});
myWorker.onmessage = (e) => {
result.textContent = e.data;
};
}// worker.js
onmessage = (e) => {
const workerResult = `Sonuç: ${e.data[0] * e.data[1]}`;
postMessage(workerResult);
};Dikkat edilecek ayrıntı şudur: ana betikte onmessage ve postMessage() worker nesnesinin üzerinden çağrılır (myWorker.postMessage), worker içinde ise doğrudan yazılır, çünkü worker'ın kendisi global kapsamdır. Mesajın içeriği her iki tarafta da olay nesnesinin data özelliğinde gelir. postMessage() tek seferde tek bir değer gönderir; birden çok değer için örnekteki gibi bir dizi ya da nesne kullanın. Gönderilecek veri önemsizse bile ilk parametre zorunludur, bu durumda açıkça null ya da undefined verilmelidir.
Webpack, Vite ve Parcel gibi paketleyiciler worker adresini new URL("worker.js", import.meta.url) biçiminde vermenizi önerir. Böylece yol HTML sayfasına değil mevcut betiğe göre çözülür ve paketleyici dosyayı güvenle yeniden adlandırabilir.
Worker() seçenekleri: type, name ve credentials

new Worker(url, options) çağrısındaki seçenek nesnesi üç özellik alır. type değeri "classic" (varsayılan) ya da "module" olabilir. Module worker bir ECMAScript modülü olarak yüklenir ve içinde import deyimini kullanabilirsiniz; modül sözdizimi için JavaScript öğrenme rehberindeki modül bölümüne bakabilirsiniz. name worker'ın global kapsamına bir ad verir ve çoğunlukla hata ayıklamada işe yarar. credentials ise module worker modül içe aktarırken kimlik bilgisi gönderilip gönderilmeyeceğini belirler; değerleri omit, same-origin (varsayılan) ve include olup classic worker'da yok sayılır.
const klasik = new Worker("worker_classic.js");
const modul = new Worker("worker_module.js", {
type: "module",
name: "rapor-hesaplayici",
});Classic worker'da dış betik yüklemenin yolu importScripts() fonksiyonudur. Sıfır ya da daha fazla adres alır, başka origin'lerden betik de yükleyebilir. Betikler herhangi bir sırayla indirilebilir ama verdiğiniz sırayla ve senkron olarak çalıştırılır: fonksiyon, tüm betikler yüklenip çalışmadan geri dönmez. Bir betik yüklenemezse NETWORK_ERROR fırlatılır ve sonraki kod çalışmaz.
| Özellik | Classic worker | Module worker |
|---|---|---|
| Oluşturma | new Worker(url) veya type: classic | new Worker(url, { type: "module" }) |
| Bağımlılık yükleme | importScripts() ile senkron | import deyimi ile |
| credentials seçeneği | Yok sayılır | omit, same-origin (varsayılan), include |
| Betik adresi | Aynı origin, blob: veya data: | Aynı origin, blob: veya data: |
Veri nasıl taşınır: structured clone algoritması

Ana sayfa ile worker arasında giden veri paylaşılmaz, kopyalanır. Gönderilen nesne bir uçta serileştirilir, diğer uçta yeniden oluşturulur; iki taraf aynı nesneye değil birbirinin kopyasına sahip olur. Bu işi structured clone algoritması yapar. Aynı algoritma structuredClone() fonksiyonunda, IndexedDB'ye nesne kaydederken ve window.postMessage() çağrısında da kullanılır.
Algoritma JSON'un taşıyamadığı bazı şeyleri taşıyabilir: döngüsel başvurular, Date, Map, Set, RegExp, ArrayBuffer, typed array'ler, Blob, File ve belirli hata türleri desteklenir. Aşağıdaki örneği Node.js 24 ile çalıştırdık; structuredClone() tarayıcıda ve Node'da aynı standarda göre çalışır.
const kayit = { ad: "Ayşe", tarih: new Date(0), etiketler: new Set(["a", "b"]) };
kayit.kendisi = kayit; // döngüsel başvuru
const kopya = structuredClone(kayit);
console.log(kopya === kayit); // false
console.log(kopya.kendisi === kopya); // true
console.log(kopya.tarih instanceof Date); // true
console.log(kopya.tarih.toISOString()); // 1970-01-01T00:00:00.000Z
console.log(kopya.etiketler.has("b")); // trueBazı şeyler ise kopyalanamaz. Function nesneleri ve DOM düğümleri DataCloneError adlı bir DOMException fırlatır. Prototip zinciri kopyalanmaz, bu yüzden bir sınıf örneği karşı tarafa düz nesne olarak gider ve yöntemlerini kaybeder. Getter, setter ve özellik tanımlayıcıları korunmaz, sınıfların özel (#) alanları kopyalanmaz, RegExp nesnesinin lastIndex değeri de taşınmaz.
try {
structuredClone({ hesapla: () => 42 });
} catch (err) {
console.log(err.name); // DataCloneError
console.log(err instanceof DOMException); // true
}
class Kullanici {
#gizli = "x";
constructor(ad) { this.ad = ad; }
selam() { return `Merhaba ${this.ad}`; }
}
const k = structuredClone(new Kullanici("Can"));
console.log(k instanceof Kullanici); // false
console.log(typeof k.selam); // undefined
console.log(JSON.stringify(k)); // {"ad":"Can"}Sahipliği devretmek: transferable nesneler
Büyük bir ikili veriyi kopyalamak hem zaman hem bellek harcar. Transferable nesneler bu sorunu çözer: ArrayBuffer gibi bir nesneyi aktarım listesine koyduğunuzda alttaki bellek kopyalanmadan karşı tarafa taşınır. Gönderen taraftaki nesne artık o kaynağa sahip değildir; MDN bunu "detached" (ayrılmış) olarak tanımlar ve nesneyi okumaya ya da yazmaya çalışmak hata fırlatır, byteLength ise 0 olur.
postMessage(message, transfer) ya da postMessage(message, { transfer }) biçimleri kullanılır. Önemli bir ayrıntı: aktarım listesi kaynağın nasıl gönderileceğini söyler, kaynağı kendisi göndermez. Aktarılan nesne mesajın içinde de yer almalıdır, yoksa karşı tarafta erişilemez ama gönderen tarafta yine de kullanılamaz hâle gelir. Aşağıda aynı mekanizmayı MessageChannel portlarıyla gösteriyoruz; worker'ın postMessage() yöntemi de iç tarafta MessagePort yöntemine devreder.
const { port1, port2 } = new MessageChannel();
port2.onmessage = (event) => {
console.log("alınan bayt:", event.data.byteLength); // alınan bayt: 8
port1.close();
};
const tampon = new ArrayBuffer(8);
console.log("önce:", tampon.byteLength); // önce: 8
port1.postMessage(tampon, [tampon]);
console.log("sonra:", tampon.byteLength); // sonra: 0Çıktı sırası öğreticidir: "sonra: 0" satırı hemen basılır, çünkü aktarım çağrı anında gerçekleşir. Mesaj ise kuyruğa eklenir ve olay döngüsünün sonraki turunda alınır, bu yüzden "alınan bayt: 8" en son görünür.
Typed array'ler (Uint8Array, Int32Array ve benzerleri) serileştirilebilir ama aktarılamaz; aktarılabilen, alttaki buffer özelliğidir. Aynı kural structuredClone(deger, { transfer }) için de geçerlidir:
const dizi = new Uint8Array([1, 2, 3, 4]);
const tasinan = structuredClone(dizi, { transfer: [dizi.buffer] });
console.log(tasinan[3]); // 4
console.log(dizi.byteLength); // 0
const baska = new Uint8Array(4);
try {
structuredClone(baska, { transfer: [baska] });
} catch (err) {
console.log(err.name); // DataCloneError
}Hata yönetimi ve worker'ı sonlandırma
Worker içinde çalışma zamanı hatası oluştuğunda ana taraftaki Worker nesnesinde error olayı tetiklenir. Olay ErrorEvent arayüzünü uygular, kabarcıklanmaz ve iptal edilebilir; varsayılan davranışı durdurmak için preventDefault() çağrılabilir. İşinize yarayacak üç alan vardır: okunabilir hata metni message, hatanın oluştuğu dosya filename ve satır numarası lineno.
myWorker.addEventListener("error", (event) => {
console.error(`${event.filename}:${event.lineno} ${event.message}`);
});
myWorker.addEventListener("messageerror", (event) => {
// Gelen mesaj seri durumdan geri çevrilemedi
});
// İş bittiğinde ya da kullanıcı iptal ettiğinde
myWorker.terminate();messageerror olayı ise worker'dan gelen bir mesaj seri durumdan geri çevrilemediğinde tetiklenir; iptal edilemez ve kabarcıklanmaz. terminate() çağrısı worker iş parçacığını hemen öldürür, worker'ın elindeki işi bitirmesini beklemez. Kurucu da hata fırlatabilir: betiğin MIME türü yanlışsa NetworkError, belge worker başlatamıyorsa ya da aynı origin kuralı çiğneniyorsa SecurityError, adres ayrıştırılamıyorsa SyntaxError. Bu DOMException adlarının tamamını Web API hata adları yazısında topladık.
Worker'lar alt worker da başlatabilir. Alt worker aynı origin'de olmalıdır ve adresi sayfaya göre değil üst worker'ın konumuna göre çözülür. Bu, bağımlılıkları worker dosyalarının yanında tutmayı kolaylaştırır.
Güvenlik: CSP ve worker adresi
Worker kendi yürütme bağlamına sahiptir ve genel olarak onu oluşturan belgenin Content Security Policy kurallarına tabi değildir. Örneğin belge script-src 'self' ile eval() kullanımını engellese bile worker içinde eval() çalışabilir. Worker'a CSP uygulamak için worker betiğini sunan yanıta ayrı bir Content-Security-Policy başlığı eklemeniz gerekir. İstisna, adresi blob: ya da data: olan worker'lardır: bunlar oluşturan belgenin CSP'sini devralır.
MDN, Worker() kurucusuna verilen adresi bir "injection sink" olarak işaretler: kullanıcıdan gelen bir adresi doğrudan worker'a vermek XSS'e kapı açar. Betik kaynaklarını CSP ile sınırlamak ve Trusted Types uygulandığında dize yerine TrustedScriptURL vermek önerilir. Genel XSS ve CSP konusu için XSS ve CSP koruma yazısını okuyabilirsiniz.
Worker türlerini karşılaştırma

| Tür | Kim kullanır | İletişim | Tipik iş |
|---|---|---|---|
| Dedicated worker | Yalnızca onu oluşturan betik | worker.postMessage ve onmessage | Ağır hesaplama, veri işleme |
| Shared worker | Aynı origin'deki birden çok pencere ve iframe | worker.port üzerinden | Sekmeler arası ortak durum |
| Service worker | Kapsamındaki sayfalar | Olaylar (fetch, push) ve mesajlar | Ağ istekleri, çevrimdışı çalışma |
Service worker da bir worker türüdür ama amacı farklıdır: sayfa ile ağ arasında vekil sunucu gibi durur. Kaydını ve yaşam döngüsünü service worker yazısında anlatıyoruz.
Sık yapılan hatalar
- Worker içinde
documentya dawindowkullanmaya çalışmak. DOM işini ana tarafa mesajla bırakın, global içinselfyazın. - Fonksiyon, DOM düğümü ya da sınıf örneği göndermek. Fonksiyon ve DOM düğümü
DataCloneErrorverir, sınıf örneği yöntemlerini kaybeder. - Aktarılan
ArrayBuffernesnesini gönderdikten sonra kullanmaya devam etmek. Aktarımdan sonrabyteLength0 olur ve okuma ya da yazma hata fırlatır. - Typed array'in kendisini aktarım listesine koymak. Listeye
dizi.buffereklenmelidir. - Worker dosyasını başka bir alan adından yüklemeye çalışmak. Kurucu aynı origin ister; gerekiyorsa aynı origin'deki bir ara worker ya da blob kullanılır.
- Shared worker portunu
addEventListener()ile dinleyipport.start()çağırmayı unutmak. - Belgenin CSP'sinin worker'ı da koruduğunu varsaymak. Worker betiğine ayrı başlık gerekir.
Sık Sorulan Sorular
Worker içinde fetch kullanabilir miyim?
Evet. fetch() ve XMLHttpRequest worker'larda kullanılabilir. Tek istisna, XHR'nin responseXML özelliğinin worker'da her zaman null olmasıdır. Ağ istekleri için Fetch API yazısına bakabilirsiniz.
Worker'a gönderdiğim nesneyi değiştirirsem karşı taraf görür mü?
Hayır. Veri kopyalanır; iki uçta ayrı nesneler vardır. Değişikliği karşı tarafa bildirmek için yeni bir mesaj göndermeniz gerekir. Aynı belleği paylaşmanın tek yolu SharedArrayBuffer kullanmaktır ve bu, çapraz origin izolasyonu gerektirir.
Worker'ı nasıl durdururum?
Ana taraftan worker.terminate() çağırırsınız; iş parçacığı hemen sonlandırılır. Yarım kalan iş tamamlanmaz, bu yüzden önemli bir sonucu kaybetmek istemiyorsanız önce worker'dan bir "bitti" mesajı bekleyin.
Worker'da localStorage kullanılabilir mi?
MDN'in worker'larda kullanılabilen API listesinde IndexedDB, Fetch ve WebSockets gibi API'ler yer alır, Web Storage ise bu listede yoktur. Service worker belgesi de localStorage senkron çalıştığı için service worker içinde kullanılamayacağını açıkça yazar. Worker'da kalıcı veri için IndexedDB tercih edin, sayfada basit anahtar değer saklama için localStorage yazısına bakın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · Service worker kaydı ve yaşam döngüsü · postMessage, MessageChannel ve BroadcastChannel · DOMException ve güvenli bağlam hataları · JavaScript event loop