मॉड्यूल वर्कर से वेब पर थ्रेड की सुविधा

वेब वर्कर में JavaScript मॉड्यूल की मदद से, अब बैकग्राउंड थ्रेड पर काम करना आसान हो गया है.

JavaScript सिंगल-थ्रेड वाला होता है. इसका मतलब है कि यह एक समय पर सिर्फ़ एक कार्रवाई कर सकता है. यह तरीका आसान है और वेब पर कई मामलों में अच्छे से काम करता है. हालांकि, जब हमें डेटा प्रोसेसिंग, पार्स करने, कंप्यूटेशन या विश्लेषण जैसे मुश्किल काम करने होते हैं, तो इससे समस्या हो सकती है. जैसे-जैसे वेब पर ऐप्लिकेशन इंस्टॉल होने की प्रोसेस ज़्यादा से ज़्यादा पेचीदा होती जाती है, मल्टी-थ्रेड की प्रोसेसिंग की ज़रूरत बढ़ती जा रही है.

वेब प्लैटफ़ॉर्म पर Web Workers 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');
  }
});

Web Worker API, ज़्यादातर ब्राउज़र में दस साल से ज़्यादा समय से उपलब्ध है. इसका मतलब है कि कर्मचारियों को ब्राउज़र के लिए बेहतरीन सपोर्ट मिलता है और वे अच्छी तरह से ऑप्टिमाइज़ होते हैं. इसका मतलब यह भी है कि वे JavaScript मॉड्यूल से बहुत पुराने हैं. वर्कर के डिज़ाइन किए जाने के समय, कोई मॉड्यूल सिस्टम नहीं था. इसलिए, वर्कर में कोड लोड करने और स्क्रिप्ट लिखने के लिए इस्तेमाल होने वाला एपीआई, सिंक्रोनस स्क्रिप्ट लोडिंग के तरीके जैसा ही है. इसे 2009 में आम तौर पर इस्तेमाल किया गया था.

इतिहास: क्लासिक वर्कर

वर्कर कंस्ट्रक्टर, एक क्लासिक स्क्रिप्ट यूआरएल लेता है, जो दस्तावेज़ के यूआरएल से मिलता-जुलता होता है. यह तुरंत नए वर्कर इंस्टेंस का रेफ़रंस दिखाता है, जिससे मैसेजिंग इंटरफ़ेस और 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() का इस्तेमाल करता है. हालांकि, अलग-अलग तरह के टकराव से बचने और डिपेंडेंसी इंपोर्ट और एक्सपोर्ट को सिम्युलेट करने के लिए, फ़ंक्शन में मॉड्यूल को रैप किया जाता है.

मॉड्यूल वर्कर डालें

JavaScript मॉड्यूल के एर्गोनॉमिक्स और परफ़ॉर्मेंस के फ़ायदों वाले वेब वर्कर के लिए, Chrome 80 में शिपिंग का एक नया मोड है. इसे मॉड्यूल वर्कर कहते हैं. 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 मॉड्यूल का इस्तेमाल करने के लिए स्विच करने का मतलब है कि सभी कोड स्ट्रिक्ट मोड में लोड होते हैं. एक और ध्यान देने लायक बदलाव यह है कि JavaScript मॉड्यूल के टॉप-लेवल स्कोप में this की वैल्यू undefined है, जबकि क्लासिक वर्कर में वैल्यू, वर्कर का ग्लोबल स्कोप होती है. अच्छी बात यह है कि हमेशा से self ग्लोबल रहा है जो ग्लोबल स्कोप का रेफ़रंस देता है. यह सर्विस वर्कर के साथ-साथ सभी तरह के वर्कर के लिए उपलब्ध है.

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 वाला विकल्प भी शामिल है.

सर्विस वर्कर का क्या होगा?

सर्विस वर्कर स्पेसिफ़िकेशन को पहले ही अपडेट कर दिया गया है, ताकि इस सुविधा को एंट्री पॉइंट के रूप में स्वीकार किया जा सके. साथ ही, मॉड्यूल वर्कर वाले ही {type:"module"} विकल्प का इस्तेमाल किया जा सके. हालांकि, इस बदलाव को ब्राउज़र में अभी लागू नहीं किया गया है. ऐसा होने के बाद, नीचे दिए गए कोड का इस्तेमाल करके JavaScript मॉड्यूल का इस्तेमाल करके सर्विस वर्कर को इंस्टैंशिएट किया जा सकता है:

navigator.serviceWorker.register('/sw.js', {
  type: 'module'
});

अब जब खास जानकारी अपडेट हो चुकी है, तो ब्राउज़र इस नई सुविधा को लागू करना शुरू कर रहे हैं. इसमें समय लगता है, क्योंकि सर्विस वर्कर में JavaScript मॉड्यूल लाने से जुड़ी कुछ और जटिलताएं हैं. कोई अपडेट ट्रिगर करना है या नहीं, यह तय करते समय सर्विस वर्कर रजिस्ट्रेशन को इंपोर्ट की गई स्क्रिप्ट के पिछले वर्शन से उनकी तुलना करनी होगी. सर्विस वर्कर के लिए इस्तेमाल किए जाने पर, JavaScript मॉड्यूल के लिए इसे लागू करना होगा. इसके अलावा, कुछ मामलों में सर्विस वर्कर को अपडेट की जांच करते समय, स्क्रिप्ट के लिए कैश मेमोरी को बायपास करने की सुविधा होनी चाहिए.

अतिरिक्त संसाधन और आगे का लेख