Safari के JavaScript इंजन में एक छिपा हुआ दोष है: जब किसी मॉड्यूल-शैली (module-style) वाले Web Worker का एंट्री स्क्रिप्ट बंडल में कहीं और इम्पोर्ट किया जाता है, तो Safari उस एंट्री स्क्रिप्ट को दूसरी बार चलाता है। यह डुप्लिकेट रन सिंगलटन स्टेट (singleton state) को तोड़ देता है, जिससे वे वर्कर्स चुपचाप खराब हो जाते हैं जो साझा कैश (shared caches) या सिंगल-इंस्टेंस ऑब्जेक्ट्स पर निर्भर करते हैं।
यह समस्या एक ब्राउज़र-आधारित वीडियो-प्रोसेसिंग ऐप विकसित करते समय सामने आई, जो ProRes फाइलों को डिकोड करने के लिए Web Workers का उपयोग करता है। Chrome और Firefox ने बिना किसी समस्या के कोड को संभाला, लेकिन Safari वीडियो लोड करने में लगातार विफल रहा। कंसोल ने केवल एक सामान्य “cannot read video” एरर दिखाया, जबकि असली कारण वर्कर का इनिशियलाइजेशन कोड दो बार चलना था, जिससे मेमोरी में एक ही मॉड्यूल की दो स्वतंत्र प्रतियां बन गईं।
यह बग कैसे प्रकट होता है
आधुनिक बंडलर (Vite, Rollup, आदि) अक्सर वर्कर की एंट्री फ़ाइल में साझा यूटिलिटीज (shared utilities) को खींच लेते हैं ताकि लेज़ी-लोडेड चंक्स (lazy-loaded chunks) उस कोड को एंट्री पॉइंट से वापस इम्पोर्ट कर सकें। उन ब्राउज़रों में जो मानक मॉड्यूल लोडर व्यवहार का पालन करते हैं, एक बार एंट्री मॉड्यूल इंस्टेंटिएट (instantiate) हो जाने के बाद, लोडर किसी भी बाद के इम्पोर्ट के लिए वही मॉड्यूल ऑब्जेक्ट लौटा देता है, जिससे दूसरी बार निष्पादन (execution) रुक जाता है।
Safari इस अपेक्षा से अलग व्यवहार करता है। जब कोई लेज़ी-लोडेड चंक वर्कर की एंट्री फ़ाइल को इम्पोर्ट करता है, तो Safari उस इम्पोर्ट को एक नए मॉड्यूल अनुरोध (fresh module request) के रूप में मानता है और एंट्री स्क्रिप्ट को फिर से चलाता है। इसका परिणाम यह होता है कि वहां परिभाषित प्रत्येक वेरिएबल, क्लास या सिंगलटन के दो अलग-अलग इंस्टेंस बन जाते हैं।
जब एंट्री दो बार चलती है तो क्या टूटता है
- सिंगलटन और कैश अब डेटा साझा नहीं करते; एक प्रति खाली कैश देखती है जबकि दूसरी उसे भरती है।
- रजिस्ट्रीज़ (उदाहरण के लिए, मैसेज हैंडलर्स की एक सूची) दो इंस्टेंस के बीच बंट जाती हैं, जिससे एक पक्ष प्रभावी रूप से खाली रह जाता है।
- इवेंट लिसनर्स दो बार अटैच हो जाते हैं, जिससे संभावित रूप से डुप्लिकेट हैंडलिंग या मेमोरी ब्लोट (memory bloat) हो सकता है।
- WebAssembly (WASM) मॉड्यूल दो बार लोड होते हैं, जिससे बैंडविड्थ और इनिशियलाइजेशन का समय बर्बाद होता है।
- विफलता शांत होती है: कोई अनकैच किया गया अपवाद (uncaught exception) नहीं आता है, केवल वह डाउनस्ट्रीम लॉजिक जो गायब स्टेट पर निर्भर करता है, गलत व्यवहार करता है।
प्रोजेक्ट में समस्या का पता लगाना
बिल्ट एसेट्स (built assets) के खिलाफ एक त्वरित grep यह बता सकता है कि क्या कोई चंक वर्कर की एंट्री फ़ाइल को इम्पोर्ट करता है:
grep -l 'from"./your.worker-' dist/assets/*.js
यदि कमांड किसी भी फ़ाइल को सूचीबद्ध करती है, तो वे इम्पोर्ट संभवतः Safari में डबल-रन बग को ट्रिगर कर रहे हैं।
व्यावहारिक समाधान
वर्कर एंट्री से साझा कोड निकालें।
बंडलर को सामान्य लाइब्रेरीज़ को अपने स्वयं के चंक में रखने के लिए कॉन्फ़िगर करें (उदाहरण के लिए, Rollup केmanualChunksका उपयोग करके)। इसके बाद वर्कर और कोई भी लेज़ी-लोडेड मॉड्यूल उस तीसरे फ़ाइल से लाइब्रेरी को इम्पोर्ट करते हैं, जिससे वर्कर के एंट्री पॉइंट को इम्पोर्ट करने की आवश्यकता समाप्त हो जाती है।एक थिन (thin) एंट्री फ़ाइल का उपयोग करें।
वर्कर के एंट्री स्क्रिप्ट को केवल एक लाइन तक सीमित कर दें जो वास्तविक कार्यान्वयन (implementation) को री-एक्सपोर्ट करती है:// worker-entry.js import("./main.js");जब तक कोई अन्य बंडल
worker-entry.jsको इम्पोर्ट नहीं करता है, Safari कभी भी दूसरा इम्पोर्ट अनुरोध नहीं देखता है, इसलिए एंट्री केवल एक बार चलती है।
दोनों दृष्टिकोण पूरे एप्लिकेशन में वर्कर के इनिशियलाइजेशन कोड को single-tonic रखते हैं।
यह बग क्यों महत्वपूर्ण है
Web Workers भारी गणनाओं (heavy computation)—जैसे वीडियो एनकोडिंग, इमेज प्रोसेसिंग, क्रिप्टोग्राफी—को मुख्य थ्रेड से दूर ले जाने के लिए एक सामान्य पैटर्न हैं। एक शांत स्टेट स्प्लिट (silent state split) एक पूरी तरह से कार्यात्मक फीचर को एक रुक-रुक कर होने वाली विफलता में बदल सकता है जो केवल Safari पर दिखाई देती है, जो डेस्कटॉप और मोबाइल उपकरणों के एक बड़े हिस्से पर डिफॉल्ट ब्राउज़र है। क्योंकि त्रुटि एक सामान्य मीडिया-लोड विफलता के रूप में सामने आती है, डेवलपर्स गलत लक्षण का पीछा करने में घंटों बिता सकते हैं।
यह बग एक व्यापक जोखिम को भी उजागर करता है: उन मॉड्यूल-लोडर सिमेंटिक्स (module-loader semantics) पर भरोसा करना जो ब्राउज़रों में समान रूप से लागू नहीं होते हैं। जब किसी बंडलर की ऑप्टिमाइज़ेशन रणनीति एक सिंगल साझा मॉड्यूल इंस्टेंस मानती है, तो कोई भी विचलन उस धारणा को तोड़ सकता है।
काउंटर-पॉइंट और खुले प्रश्न
Safari का व्यवहार उसके अपने मॉड्यूल रेज़ोल्यूशन नियमों के अनुरूप है, जो वर्कर्स से जुड़े एज केस (edge cases) में स्पेसिफिकेशन (spec) से सूक्ष्म रूप से भिन्न होते हैं। कुछ डेवलपर्स का तर्क है कि बंडलर को वर्कर की एंट्री फ़ाइल में साझा कोड रखने से पूरी तरह बचना चाहिए, जिससे यह मुद्दा ब्राउज़र दोष के बजाय बिल्ड-टाइम अनुशासन का मामला बन जाता है। अन्य लोग बताते हैं कि Safari का विचलन प्रलेखित (documented) नहीं है, जिससे डेवलपर्स के पास इसका अनुमान लगाने का कोई विश्वसनीय तरीका नहीं बचता है।
Apple ने सार्वजनिक रूप से इस समस्या को स्वीकार नहीं किया है, और इसके समाधान के लिए कोई ज्ञात समयसीमा नहीं है। जब तक Safari अपना लोडर नहीं बदलता, तब तक अपने बंडलों को पुनर्गठित करने या अपने CI पाइपलाइनों में डिटेक्शन लॉजिक जोड़ने की जिम्मेदारी डेवलपर्स पर बनी रहती है।
आगे क्या देखें
- Browser updates – module-worker हैंडलिंग के किसी भी उल्लेख के लिए Safari रिलीज़ नोट्स पर नज़र रखें।
- Bundler community patches – Vite, Rollup और अन्य बंडलर उस पैटर्न से बचने के लिए चेतावनी या ऑटोमैटिक चंकिंग रणनीतियाँ पेश कर सकते हैं जो इस बग को ट्रिगर करता है।
- Testing practices – रिलीज़ से पहले Safari पर वास्तविक मीडिया फ़ाइलों और फुल-स्टैक वर्कर टेस्ट को शामिल करने से इस साइलेंट फेलियर का जल्दी पता लगाया जा सकता है।
मुख्य निष्कर्ष
यदि आपके Safari उपयोगकर्ता वर्कर से संबंधित अस्पष्ट विफलताओं का अनुभव करते हैं, तो जाँचें कि क्या कोई नॉन-वर्कर बंडल वर्कर के एंट्री स्क्रिप्ट को इम्पोर्ट कर रहा है। डबल एग्जीक्यूशन बग साइलेंटली सिंगलटन स्टेट को खराब कर देता है, लेकिन साझा कोड को एंट्री पॉइंट से बाहर ले जाने या एंट्री को एक पतले री-एक्सपोर्ट (thin re-export) में बदलने से ब्राउज़र फिक्स का इंतज़ार किए बिना सही व्यवहार बहाल हो जाता है।
