Enjin JavaScript Safari mempunyai kecacatan tersembunyi: apabila skrip entri Web Worker gaya modul diimport di mana-mana bahagian lain dalam bundle, Safari melaksanakan skrip entri tersebut buat kali kedua. Pelaksanaan pendua ini merosakkan keadaan singleton, mensabotaj secara senyap worker yang bergantung pada cache kongsi atau objek satu-instans.
Masalah ini muncul semasa membangunkan aplikasi pemprosesan video berasaskan pelayar yang menggunakan Web Worker untuk menyahkod fail ProRes. Chrome dan Firefox mengendalikan kod tersebut tanpa sebarang masalah, tetapi Safari secara konsisten gagal memuatkan video. Konsol hanya melaporkan ralat umum “cannot read video”, manakala punca sebenarnya adalah kod inisialisasi worker yang berjalan dua kali dan meninggalkan dua salinan modul yang sama dalam memori.
Bagaimana pepijat ini muncul
Bundler moden (Vite, Rollup, dll.) sering menarik utiliti kongsi ke dalam fail entri worker supaya chunk yang dimuat turun secara malas (lazy-loaded) boleh mengimport semula kod tersebut daripada titik entri. Dalam pelayar yang mengikut tingkah laku pemuat modul standard, sebaik sahaja modul entri diinstansiasi, pemuat akan mengembalikan objek modul yang sama kepada sebarang import seterusnya, sekali gus menghalang pelaksanaan kedua.
Safari menyimpang daripada jangkaan tersebut. Apabila chunk yang dimuat turun secara malas mengimport fail entri worker, Safari menganggap import tersebut sebagai permintaan modul baharu dan melaksanakan semula skrip entri. Hasilnya ialah dua instans berasingan bagi setiap pemboleh ubah, kelas, atau singleton yang ditakrifkan di sana.
Apa yang rosak apabila entri berjalan dua kali
- Singleton dan cache tidak lagi berkongsi data; satu salinan melihat cache kosong manakala satu lagi mengisinya.
- Registry (contohnya, senarai pengendali mesej) akhirnya terbahagi antara dua instans, menyebabkan satu bahagian menjadi kosong secara efektif.
- Pendengar acara (event listeners) dipasang dua kali, berpotensi menyebabkan pengendalian pendua atau pembengkakan memori.
- Modul WebAssembly (WASM) dimuatkan dua kali, membazirkan jalur lebar dan masa inisialisasi.
- Kegagalan ini berlaku secara senyap: tiada pengecualian (exception) yang tidak ditangkap dilemparkan, hanya logik hiliran yang bergantung pada keadaan yang hilang akan bertindak salah.
Mengesan isu dalam projek
Grep pantas terhadap aset yang telah dibina boleh mendedahkan sama ada mana-mana chunk mengimport fail entri worker:
grep -l 'from"./your.worker-' dist/assets/*.js
Jika arahan tersebut menyenaraikan sebarang fail, import tersebut berkemungkinan mencetuskan pepijat pelaksanaan dua kali dalam Safari.
Jalan penyelesaian praktikal
Ekstrak kod kongsi daripada entri worker.
Konfigurasikan bundler untuk meletakkan perpustakaan (library) biasa ke dalam chunk mereka sendiri (contohnya, menggunakanmanualChunksRollup). Kedua-dua worker dan mana-mana modul yang dimuat turun secara malas kemudian mengimport perpustakaan tersebut daripada fail ketiga itu, sekali gus menghapuskan keperluan untuk mengimport titik entri worker.Gunakan fail entri yang nipis.
Kurangkan skrip entri worker kepada satu baris yang mengeksport semula pelaksanaan sebenar:// worker-entry.js import("./main.js");Selagi tiada bundle lain mengimport
worker-entry.js, Safari tidak akan melihat permintaan import kedua, jadi entri hanya berjalan sekali.
Kedua-dua pendekatan memastikan kod inisialisasi worker kekal sebagai singleton di seluruh aplikasi.
Mengapa pepijat ini penting
Web Worker adalah corak biasa untuk mengalihkan pengiraan berat—penyulitan video, pemprosesan imej, kriptografi—daripada thread utama. Perpecahan keadaan (state split) yang senyap boleh mengubah ciri yang berfungsi dengan sempurna menjadi kegagalan berselang-seli yang hanya muncul pada Safari, pelayar lalai bagi sebahagian besar peranti desktop dan mudah alih. Oleh kerana ralat muncul sebagai kegagalan pemuatan media yang umum, pembangun mungkin menghabiskan masa berjam-jam mengejar simptom yang salah.
Pepijat ini juga menonjolkan risiko yang lebih luas: bergantung pada semantik pemuat modul yang tidak dilaksanakan secara seragam merentas pelayar. Apabila strategi pengoptimuman bundler mengandaikan satu instans modul kongsi tunggal, sebarang penyimpangan boleh merosakkan andaian tersebut.
Hujah balas dan persoalan terbuka
Tingkah laku Safari selaras dengan peraturan resolusi modulnya sendiri, yang berbeza sedikit daripada spesifikasi dalam kes pinggiran (edge cases) yang melibatkan worker. Sesetengah pembangun berpendapat bahawa bundler sepatutnya mengelak daripada meletakkan kod kongsi dalam fail entri worker sama sekali, menjadikan isu ini sebagai masalah disiplin masa binaan (build-time) dan bukannya kecacatan pelayar. Yang lain menyatakan bahawa penyimpangan Safari tidak didokumentasikan, menyebabkan pembangun tidak mempunyai cara yang boleh dipercayai untuk menjangkanya.
Apple belum mengakui masalah ini secara terbuka, dan tiada garis masa yang diketahui untuk pembaikan. Sehingga Safari mengubah pemuatnya, tanggungjawab terletak pada pembangun untuk menyusun semula bundle mereka atau menambah logik pengesanan ke dalam saluran paip (pipeline) CI mereka.
Apa yang perlu diperhatikan seterusnya
- Kemas kini pelayar – Pantau nota keluaran Safari untuk sebarang sebutan mengenai pengendalian module-worker.
- Tampalan komuniti bundler – Vite, Rollup, dan lain-lain mungkin memperkenalkan amaran atau strategi chunking automatik untuk mengelakkan corak yang mencetuskan pepijat tersebut.
- Amalan pengujian – Menggabungkan fail media dunia sebenar dan ujian worker full-stack pada Safari sebelum pelancaran boleh mengesan kegagalan senyap lebih awal.
Rumusan
Jika pengguna Safari anda mengalami kegagalan berkaitan worker yang tidak dapat dijelaskan, semak sama ada mana-mana bundle bukan-worker mengimport skrip kemasukan (entry script) worker tersebut. Pepijat pelaksanaan berganda secara senyap merosakkan keadaan singleton, tetapi memindahkan kod kongsi keluar daripada titik kemasukan atau meringkaskan kemasukan kepada re-export yang ringkas dapat memulihkan tingkah laku yang betul tanpa perlu menunggu pembaikan pelayar.
