Safari’nin JavaScript motorunda gizli bir kusur var: modül tarzı bir Web Worker’ın giriş betiği paketin (bundle) başka bir yerinde içe aktarıldığında, Safari bu giriş betiğini ikinci kez çalıştırıyor. Bu mükerrer çalışma, singleton durumunu bozarak paylaşılan önbelleklere veya tekil örnek (single-instance) nesnelere güvenen worker'ları sessizce sabote ediyor.
Sorun, ProRes dosyalarını çözmek için Web Worker'ları kullanan tarayıcı tabanlı bir video işleme uygulaması geliştirirken ortaya çıktı. Chrome ve Firefox kodu sorunsuz bir şekilde işlerken, Safari videoyu yüklemeyi sürekli başaramadı. Konsol yalnızca genel bir “video okunamıyor” hatası bildirirken, asıl suçlu worker'ın başlatma kodunun iki kez çalışması ve bellekte aynı modülün iki bağımsız kopyasını bırakmasıydı.
Hata nasıl kendini gösteriyor
Modern paketleyiciler (Vite, Rollup vb.), tembel yüklenen (lazy-loaded) parçaların (chunks) bu kodu giriş noktasından geri içe aktarabilmesi için genellikle paylaşılan yardımcı araçları worker'ın giriş dosyasına çeker. Standart modül yükleyici davranışını izleyen tarayıcılarda, giriş modülü örneklendiğinde, yükleyici sonraki tüm içe aktarmalar için aynı modül nesnesini döndürerek ikinci bir yürütmeyi engeller.
Safari bu beklentiden ayrılıyor. Tembel yüklenen bir parça worker'ın giriş dosyasını içe aktardığında, Safari bu içe aktarmayı yeni bir modül isteği olarak değerlendiriyor ve giriş betiğini yeniden çalıştırıyor. Sonuç, orada tanımlanan her değişken, sınıf veya singleton için iki ayrı örnektir.
Giriş iki kez çalıştığında neler bozulur
- Singleton'lar ve önbellekler artık veri paylaşmaz; bir kopya boş bir önbellek görürken diğeri onu doldurur.
- Kayıt defterleri (Registries) (örneğin, bir mesaj işleyici listesi) iki örnek arasında bölünür ve bir taraf etkili bir şekilde boş kalır.
- Olay dinleyicileri (Event listeners) iki kez eklenir, bu da potansiyel olarak mükerrer işleme veya bellek şişmesine neden olabilir.
- WebAssembly (WASM) modülleri iki kez yüklenerek bant genişliğini ve başlatma süresini boşa harcar.
- Hata sessizdir: yakalanmamış bir hata fırlatılmaz, yalnızca eksik duruma bağlı olan sonraki mantık (downstream logic) hatalı çalışır.
Bir projede sorunu tespit etme
Derlenmiş varlıklar (assets) üzerinde yapılacak hızlı bir grep araması, herhangi bir parçanın worker'ın giriş dosyasını içe aktarıp aktarmadığını ortaya çıkarabilir:
grep -l 'from"./your.worker-' dist/assets/*.js
Eğer komut herhangi bir dosya listelerse, bu içe aktarmalar Safari'deki çift çalıştırma hatasını tetikliyor olabilir.
Pratik çözüm yolları
Paylaşılan kodu worker girişinden ayırın.
Paketleyiciyi, ortak kütüphaneleri kendi parçalarına yerleştirecek şekilde yapılandırın (örneğin, Rollup’ınmanualChunksözelliğini kullanarak). Hem worker hem de tüm tembel yüklenen modüller, kütüphaneyi bu üçüncü dosyadan içe aktarır; böylece worker'ın giriş noktasını içe aktarma ihtiyacı ortadan kalkar.İnce bir giriş dosyası kullanın.
Worker'ın giriş betiğini, gerçek uygulamayı yeniden dışa aktaran (re-export) tek bir satıra indirin:// worker-entry.js import("./main.js");Başka hiçbir paket
worker-entry.jsdosyasını içe aktarmadığı sürece, Safari ikinci bir içe aktarma isteği görmez ve böylece giriş yalnızca bir kez çalışır.
Her iki yaklaşım da worker'ın başlatma kodunun tüm uygulama boyunca tekil kalmasını sağlar.
Bu hata neden önemli
Web Worker'lar; video kodlama, görüntü işleme, kriptografi gibi ağır hesaplamaları ana iş parçacığından (main thread) uzaklaştırmak için yaygın bir modeldir. Sessiz bir durum bölünmesi, mükemmel çalışan bir özelliği, masaüstü ve mobil cihazların büyük bir kısmında varsayılan tarayıcı olan Safari'de ortaya çıkan aralıklı bir hataya dönüştürebilir. Hata genel bir medya yükleme hatası olarak göründüğü için, geliştiriciler saatlerce yanlış belirtinin peşinden koşabilirler.
Hata aynı zamanda daha geniş bir riski de vurguluyor: Tarayıcılar arasında tek tip olarak uygulanmayan modül yükleyici semantiğine güvenmek. Bir paketleyicinin optimizasyon stratejisi tek bir paylaşılan modül örneği varsaydığında, herhangi bir sapma bu varsayımı bozabilir.
Karşı görüş ve açık sorular
Safari'nin davranışı, worker'ları içeren uç durumlarda spesifikasyondan (spec) ince farklarla ayrılan kendi modül çözümleme kurallarıyla uyumludur. Bazı geliştiriciler, paketleyicilerin paylaşılan kodu bir worker'ın giriş dosyasına koymaktan tamamen kaçınması gerektiğini, bu nedenle sorunun bir tarayıcı kusurundan ziyade derleme zamanı disiplini meselesi olduğunu savunuyor. Diğerleri ise Safari'nin bu sapmasının belgelenmediğine ve geliştiricilerin bunu öngörmek için güvenilir bir yolları olmadığına dikkat çekiyor.
Apple sorunu kamuoyuna açıklamadı ve bir düzeltme için bilinen bir zaman çizelgesi bulunmuyor. Safari yükleyicisini değiştirene kadar, paketleri yeniden yapılandırmak veya CI süreçlerine tespit mantığı eklemek geliştiricilerin sorumluluğunda kalmaya devam ediyor.
Bundan sonra neye dikkat edilmeli
- Tarayıcı güncellemeleri – module-worker işleme ile ilgili herhangi bir ibare için Safari sürüm notlarını takip edin.
- Bundler topluluk yamaları – Vite, Rollup ve diğerleri, hatayı tetikleyen kalıptan kaçınmak için uyarılar veya otomatik chunking stratejileri getirebilir.
- Test uygulamaları – Yayınlamadan önce gerçek dünya medya dosyalarını ve Safari üzerinde tam yığın (full-stack) worker testlerini sürece dahil etmek, sessiz hatayı erkenden yakalayabilir.
Özet
Safari kullanıcılarınız açıklanamayan worker kaynaklı hatalar yaşıyorsa, worker olmayan herhangi bir bundle'ın worker'ın giriş (entry) betiğini içe aktarıp aktarmadığını kontrol edin. Çift yürütme hatası singleton state'i sessizce bozar; ancak paylaşılan kodu giriş noktasından (entry point) dışarı taşımak veya girişi ince bir re-export'a dönüştürmek, bir tarayıcı düzeltmesi beklemeden doğru davranışı geri kazandırır.
