वेब वर्कर में JavaScript मॉड्यूल की मदद से, बैकग्राउंड थ्रेड में ज़्यादा काम करना अब आसान हो गया है.
JavaScript एक-थ्रेड वाला है. इसका मतलब है कि यह एक बार में सिर्फ़ एक कार्रवाई कर सकता है. यह तरीका आसान है और वेब पर कई मामलों में अच्छा काम करता है. हालांकि, जब हमें डेटा प्रोसेस करने, पार्स करने, कैलकुलेशन करने या विश्लेषण करने जैसे मुश्किल काम करने होते हैं, तो यह समस्या पैदा कर सकता है. वेब पर ज़्यादा से ज़्यादा मुश्किल ऐप्लिकेशन डिलीवर किए जा रहे हैं. इसलिए, मल्टी-थ्रेड प्रोसेसिंग की ज़रूरत बढ़ रही है.
वेब प्लैटफ़ॉर्म पर, थ्रेडिंग और पैरलल प्रोसेसिंग के लिए मुख्य प्राइमटिव, Web Worker API है. वर्कर्स, ऑपरेटिंग सिस्टम की थ्रेड के ऊपर एक लाइटवाइट एब्स्ट्रैक्शन होते हैं. ये थ्रेड के बीच कम्यूनिकेशन के लिए, मैसेज पास करने वाला एपीआई एक्सपोज़ करते हैं. यह सुविधा, बड़े डेटासेट पर काम करने या महंगे कैलकुलेशन करते समय काफ़ी मददगार हो सकती है. इससे एक या उससे ज़्यादा बैकग्राउंड थ्रेड पर महंगे ऑपरेशन करते समय, मुख्य थ्रेड को आसानी से चलाया जा सकता है.
यहां वर्कर के इस्तेमाल का एक सामान्य उदाहरण दिया गया है. इसमें, वर्कर स्क्रिप्ट मुख्य थ्रेड से मैसेज सुनती है और अपने मैसेज भेजकर जवाब देती है:
page.js:
const worker = new Worker('worker.js');
worker.addEventListener('message', e => {
console.log(e.data);
});
worker.postMessage('hello');
worker.js:
addEventListener('message', e => {
if (e.data === 'hello') {
postMessage('world');
}
});
वेब वर्कर्स एपीआई, ज़्यादातर ब्राउज़र में 10 साल से ज़्यादा समय से उपलब्ध है. इसका मतलब है कि वर्कर को ब्राउज़र की बेहतर सुविधा मिलती है और वे अच्छी तरह से ऑप्टिमाइज़ किए जाते हैं. हालांकि, इसका यह भी मतलब है कि वे JavaScript मॉड्यूल से पहले के वर्शन हैं. वर्कर डिज़ाइन किए जाने के समय कोई मॉड्यूल सिस्टम नहीं था. इसलिए, वर्कर में कोड लोड करने और स्क्रिप्ट लिखने के लिए एपीआई, 2009 में आम तौर पर इस्तेमाल होने वाली सिंक्रोनस स्क्रिप्ट लोड करने के तरीकों से मिलता-जुलता है.
इतिहास: क्लासिक वर्कर्स
Worker कन्स्ट्रक्टर में क्लासिक स्क्रिप्ट का यूआरएल डाला जाता है. यह यूआरएल, दस्तावेज़ के यूआरएल से जुड़ा होता है. यह तुरंत नए वर्कर इंस्टेंस का रेफ़रंस दिखाता है. इसमें मैसेजिंग इंटरफ़ेस के साथ-साथ terminate()
तरीका भी दिखता है. यह तरीका, वर्कर को तुरंत बंद कर देता है और उसे मिटा देता है.
const worker = new Worker('worker.js');
अतिरिक्त कोड लोड करने के लिए, वेब वर्कर्स में importScripts()
फ़ंक्शन उपलब्ध है. हालांकि, यह हर स्क्रिप्ट को फ़ेच और उसका आकलन करने के लिए, वर्कर्स के एक्सीक्यूशन को रोक देता है. यह क्लासिक <script>
टैग की तरह, ग्लोबल स्कोप में भी स्क्रिप्ट को लागू करता है. इसका मतलब है कि एक स्क्रिप्ट के वैरिएबल, किसी दूसरी स्क्रिप्ट के वैरिएबल से बदले जा सकते हैं.
worker.js:
importScripts('greet.js');
// ^ could block for seconds
addEventListener('message', e => {
postMessage(sayHello());
});
greet.js:
// global to the whole worker
function sayHello() {
return 'world';
}
इस वजह से, वेब वर्कर्स का ऐप्लिकेशन के आर्किटेक्चर पर काफ़ी असर पड़ता है. डेवलपर को बेहतर टूल और तरीके बनाने पड़े हैं, ताकि वे आधुनिक डेवलपमेंट के तरीकों को छोड़े बिना, वेब वर्कर्स का इस्तेमाल कर सकें. उदाहरण के लिए, webpack जैसे बंडलर, जनरेट किए गए कोड में एक छोटा मॉड्यूल लोडर लागू करते हैं. यह कोड लोड करने के लिए importScripts()
का इस्तेमाल करता है. हालांकि, वैरिएबल के आपस में टकराने से बचने और डिपेंडेंसी इंपोर्ट और एक्सपोर्ट को सिम्युलेट करने के लिए, मॉड्यूल को फ़ंक्शन में रैप करता है.
मॉड्यूल वर्कर्स डालें
Chrome 80 में, वेब वर्कर्स के लिए एक नया मोड उपलब्ध है. इसे मॉड्यूल वर्कर्स कहा जाता है. इसमें JavaScript के मॉड्यूल की परफ़ॉर्मेंस और इस्तेमाल करने में आसानी के फ़ायदे मिलते हैं. Worker
कॉन्स्ट्रक्टर अब एक नया {type:"module"}
विकल्प स्वीकार करता है. यह विकल्प, <script type="module">
से मैच करने के लिए स्क्रिप्ट लोड करने और उसे लागू करने का तरीका बदलता है.
const worker = new Worker('worker.js', {
type: 'module'
});
मॉड्यूल वर्कर्स, स्टैंडर्ड JavaScript मॉड्यूल होते हैं. इसलिए, वे इंपोर्ट और एक्सपोर्ट स्टेटमेंट का इस्तेमाल कर सकते हैं. सभी JavaScript मॉड्यूल की तरह ही, डिपेंडेंसी किसी दिए गए कॉन्टेक्स्ट (मुख्य थ्रेड, वर्कर वगैरह) में सिर्फ़ एक बार लागू की जाती हैं. साथ ही, आने वाले समय में किए जाने वाले सभी इंपोर्ट, पहले से लागू मॉड्यूल इंस्टेंस का रेफ़रंस देते हैं. ब्राउज़र, JavaScript मॉड्यूल को लोड करने और उन्हें चलाने की प्रोसेस को भी ऑप्टिमाइज़ करते हैं. मॉड्यूल को लागू करने से पहले, उसकी डिपेंडेंसी को लोड किया जा सकता है. इससे पूरे मॉड्यूल ट्री को एक साथ लोड किया जा सकता है. मॉड्यूल लोड करने पर, पार्स किए गए कोड को भी कैश मेमोरी में सेव किया जाता है. इसका मतलब है कि मुख्य धागे और वर्कर में इस्तेमाल किए जाने वाले मॉड्यूल को सिर्फ़ एक बार पार्स करना पड़ता है.
JavaScript मॉड्यूल पर स्विच करने से, डाइनैमिक इंपोर्ट का इस्तेमाल भी किया जा सकता है. इससे, कोड को धीरे-धीरे लोड करने के लिए, वर्कर्स को ब्लॉक किए बिना काम किया जा सकता है. डाइनैमिक इंपोर्ट, डिपेंडेंसी लोड करने के लिए importScripts()
का इस्तेमाल करने के मुकाबले ज़्यादा साफ़ तौर पर काम करता है. ऐसा इसलिए है, क्योंकि इंपोर्ट किए गए मॉड्यूल के एक्सपोर्ट को दिखाया जाता है, न कि ग्लोबल वैरिएबल पर भरोसा किया जाता है.
worker.js:
import { sayHello } from './greet.js';
addEventListener('message', e => {
postMessage(sayHello());
});
greet.js:
import greetings from './data.js';
export function sayHello() {
return greetings.hello;
}
बेहतर परफ़ॉर्मेंस के लिए, मॉड्यूल वर्कर्स में importScripts()
का पुराना तरीका उपलब्ध नहीं है. वर्कर्स को JavaScript मॉड्यूल का इस्तेमाल करने के लिए स्विच करने का मतलब है कि सारा कोड strict
mode में लोड होता है. एक और अहम बदलाव यह है कि JavaScript मॉड्यूल के टॉप-लेवल स्कोप में this
की वैल्यू undefined
है, जबकि क्लासिक वर्कर्स में वैल्यू, वर्कर्स का ग्लोबल स्कोप है. सौभाग्य से, self
ग्लोबल हमेशा से ही मौजूद रहा है, जो ग्लोबल स्कोप का रेफ़रंस देता है. यह सर्विस वर्कर के साथ-साथ, सभी तरह के वर्कर और DOM में उपलब्ध है.
modulepreload
के साथ वर्कर्स को पहले से लोड करना
मॉड्यूल वर्कर्स की मदद से, परफ़ॉर्मेंस में एक अहम सुधार होता है. यह सुधार, वर्कर्स और उनकी डिपेंडेंसी को पहले से लोड करने की सुविधा से मिलता है. मॉड्यूल वर्कर की मदद से, स्क्रिप्ट को स्टैंडर्ड JavaScript मॉड्यूल के तौर पर लोड और चलाया जाता है. इसका मतलब है कि उन्हें पहले से लोड किया जा सकता है. साथ ही, modulepreload
का इस्तेमाल करके, उन्हें पहले से पार्स भी किया जा सकता है:
<!-- preloads worker.js and its dependencies: -->
<link rel="modulepreload" href="worker.js">
<script>
addEventListener('load', () => {
// our worker code is likely already parsed and ready to execute!
const worker = new Worker('worker.js', { type: 'module' });
});
</script>
पहले से लोड किए गए मॉड्यूल का इस्तेमाल, मुख्य थ्रेड और मॉड्यूल वर्कर, दोनों कर सकते हैं. यह उन मॉड्यूल के लिए फ़ायदेमंद है जिन्हें दोनों संदर्भों में इंपोर्ट किया जाता है. इसके अलावा, यह उन मामलों में भी फ़ायदेमंद है जहां यह पहले से पता नहीं चलता कि किसी मॉड्यूल का इस्तेमाल मुख्य थ्रेड पर किया जाएगा या वर्कर्स में.
पहले, वेब वर्कर स्क्रिप्ट को पहले से लोड करने के विकल्प सीमित थे और ज़रूरी नहीं कि वे भरोसेमंद हों. क्लासिक वर्कर्स के पास, प्रीलोड करने के लिए अपना "वर्कर्स" रिसॉर्स टाइप था. हालांकि, किसी भी ब्राउज़र ने <link rel="preload" as="worker">
को लागू नहीं किया. इस वजह से, वेब वर्कर्स को प्रीलोड करने के लिए, <link rel="prefetch">
का इस्तेमाल करना मुख्य तकनीक थी. यह पूरी तरह से एचटीटीपी कैश पर निर्भर करती थी. सही कैश मेमोरी हेडर के साथ इस्तेमाल करने पर, वर्कर स्क्रिप्ट को डाउनलोड करने के लिए इंतज़ार किए बिना, वर्कर इंस्टैंशिएशन को शुरू किया जा सकता है. हालांकि, modulepreload
के उलट, इस तकनीक में डिपेंडेंसी को पहले से लोड करने या पहले से पार्स करने की सुविधा काम नहीं करती थी.
शेयर किए गए कर्मचारियों के लिए क्या होगा?
शेयर किए गए वर्कर्स को, Chrome 83 के साथ JavaScript मॉड्यूल के लिए अपडेट किया गया है. खास वर्कर की तरह ही, {type:"module"}
विकल्प का इस्तेमाल करके शेयर किया गया वर्कर बनाने पर, वर्कर स्क्रिप्ट अब क्लासिक स्क्रिप्ट के बजाय मॉड्यूल के तौर पर लोड होती है:
const worker = new SharedWorker('/worker.js', {
type: 'module'
});
JavaScript मॉड्यूल के साथ काम करने से पहले, SharedWorker()
कन्स्ट्रक्टर में सिर्फ़ यूआरएल और name
आर्ग्युमेंट (ज़रूरी नहीं) होना चाहिए. यह क्लासिक शेयर किए गए वर्कर्स के इस्तेमाल के लिए काम करता रहेगा. हालांकि, मॉड्यूल शेयर किए गए वर्कर्स बनाने के लिए, नए options
आर्ग्युमेंट का इस्तेमाल करना ज़रूरी है. उपलब्ध विकल्प, एक ही तरह के होते हैं. इनमें name
विकल्प भी शामिल है, जो पिछले name
आर्ग्युमेंट की जगह लेता है.
सर्विस वर्कर के बारे में क्या?
सर्विस वर्कर स्पेसिफ़िकेशन को पहले ही अपडेट कर दिया गया है, ताकि किसी JavaScript मॉड्यूल को एंट्री पॉइंट के तौर पर स्वीकार किया जा सके. इसके लिए, मॉड्यूल वर्कर के तौर पर उसी {type:"module"}
विकल्प का इस्तेमाल किया जाता है. हालांकि, इस बदलाव को अब तक ब्राउज़र में लागू नहीं किया गया है. ऐसा होने के बाद, यहां दिए गए कोड का इस्तेमाल करके, JavaScript मॉड्यूल का इस्तेमाल करके किसी सेवा वर्कर को इंस्टैंशिएट किया जा सकता है:
navigator.serviceWorker.register('/sw.js', {
type: 'module'
});
स्पेसिफ़िकेशन अपडेट हो जाने के बाद, ब्राउज़र नए तरीके से काम करना शुरू कर रहे हैं. इसमें समय लगता है, क्योंकि JavaScript मॉड्यूल को सेवा वर्कर में शामिल करने से जुड़ी कुछ अतिरिक्त समस्याएं होती हैं. अपडेट को ट्रिगर करने का फ़ैसला करते समय, सेवा वर्कर रजिस्ट्रेशन को इंपोर्ट की गई स्क्रिप्ट की तुलना, कैश मेमोरी में सेव किए गए उनके पिछले वर्शन से करनी होगी. साथ ही, सेवा वर्कर के लिए इस्तेमाल किए जाने पर, इसे JavaScript मॉड्यूल के लिए लागू करना होगा. साथ ही, अपडेट की जांच करते समय कुछ मामलों में, सेवा वर्कर को स्क्रिप्ट के लिए कैश मेमोरी को बायपास करना होगा.