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

वेब वर्कर में 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 में इस्तेमाल किए जाने वाले सिंक्रोनस स्क्रिप्ट लोडिंग के तरीकों जैसा ही रहा है.

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

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 मॉड्यूल पर माइग्रेट करने से, dynamic import का इस्तेमाल किया जा सकता है. इससे वर्कर के एक्ज़ीक्यूशन को ब्लॉक किए बिना, कोड को लेज़ी लोड किया जा सकता है. डाइनैमिक इंपोर्ट, डिपेंडेंसी लोड करने के लिए 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;
}

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

अतिरिक्त संसाधन और ज़्यादा जानकारी