ब्राउज़र के मुख्य थ्रेड से JavaScript चलाने के लिए वेब वर्कर का इस्तेमाल करें

मुख्य थ्रेड के बाहर के आर्किटेक्चर से, आपके ऐप्लिकेशन को ज़्यादा भरोसेमंद बनाने और उपयोगकर्ता अनुभव को बेहतर बनाने में मदद मिलती है.

सुर्मा
सूरमा

पिछले 20 सालों में, कुछ स्टाइल और इमेज वाले स्टैटिक दस्तावेज़ों से लेकर, वेब में काफ़ी बदलाव आया है. अब यह बहुत जटिल और डाइनैमिक ऐप्लिकेशन बन गया है. हालांकि, अब तक एक बात में ज़्यादा बदलाव नहीं किया गया है: अपनी साइटों को रेंडर करने और JavaScript को चलाने के लिए, हमारे पास हर ब्राउज़र टैब के लिए सिर्फ़ एक थ्रेड है. हालांकि, इसमें कुछ अपवाद भी शामिल हैं.

इस वजह से, मुख्य थ्रेड पर बहुत ज़्यादा काम करना पड़ा. जैसे-जैसे वेब ऐप्लिकेशन की जटिलता बढ़ती जा रही है, मुख्य थ्रेड परफ़ॉर्मेंस के लिए बड़ा रुकावट बन जाता है. मामलों की स्थिति बदतर हो जाती है. इसलिए, किसी उपयोगकर्ता के लिए, मुख्य थ्रेड पर कोड चलाने में लगने वाला समय बिलकुल भी अनुमान नहीं लगाया जा सकता, क्योंकि डिवाइस की सुविधाओं का परफ़ॉर्मेंस पर काफ़ी असर पड़ता है. यह संभावना सिर्फ़ तब बढ़ेगी, जब उपयोगकर्ता तेज़ी से अलग-अलग तरह के डिवाइसों से वेब ऐक्सेस करेंगे. इनमें, बहुत ज़्यादा सीमित फ़ीचर फ़ोन से लेकर हाई-पावर और रीफ़्रेश-रेट वाली फ़्लैगशिप मशीनें शामिल हैं.

अगर हम चाहते हैं कि बेहतरीन वेब ऐप्लिकेशन, वेबसाइट की परफ़ॉर्मेंस की जानकारी, जैसे कि वेबसाइट की परफ़ॉर्मेंस की जानकारी देने वाले दिशा-निर्देशों को भरोसेमंद तरीके से पूरा करें, जो लोगों की नज़रों और मनोविज्ञान से जुड़े अनुभव वाले डेटा पर आधारित हैं, तो हमें अपने कोड को लागू करने के मुख्य थ्रेड (OMT) के तरीके की ज़रूरत होगी.

वेब वर्कर ही क्यों?

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

जहां वेबसाइट की परफ़ॉर्मेंस की जानकारी मौजूद है, वहां मुख्य थ्रेड से अलग काम करने से फ़ायदा हो सकता है. खास तौर पर, जब मुख्य थ्रेड से वेब वर्कर को ऑफ़लोड किया जाता है, तो मुख्य थ्रेड के लिए विवाद कम हो सकता है. इससे, रिस्पॉन्स देने वाली अहम मेट्रिक, जैसे कि इंटरैक्शन टू नेक्स्ट पेंट (आईएनपी) और फ़र्स्ट इनपुट डिले (एफ़आईडी). जब मुख्य थ्रेड को प्रोसेस करने पर कम काम करना हो, तो यह उपयोगकर्ता के इंटरैक्शन पर ज़्यादा तेज़ी से जवाब दे सकता है.

खास तौर पर, स्टार्टअप के दौरान कम काम करते हैं. लंबे टास्क को कम करके, सबसे बड़े कॉन्टेंटफ़ुल पेंट (एलसीपी) को भी फ़ायदा मिल सकता है. किसी एलसीपी एलिमेंट को रेंडर करने के लिए, मुख्य थ्रेड में लगने वाले समय की ज़रूरत होती है. टेक्स्ट या इमेज को रेंडर करने के लिए ऐसा करना अक्सर ज़रूरी होता है. ये टेक्स्ट या इमेज आम तौर पर इस्तेमाल होने वाले एलिमेंट होते हैं. साथ ही, मुख्य थ्रेड के काम को कम करके, यह पक्का किया जा सकता है कि आपके पेज के एलसीपी एलिमेंट को रेंडर होने में लगने वाले महंगे काम से ब्लॉक होने की संभावना कम हो. यह ऐसा काम है जिसे कोई वेब वर्कर मैनेज कर सकता है.

वेब वर्कर से थ्रेड की सुविधा

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

JavaScript में, हम वेब वर्कर से करीब-करीब मिलती-जुलती सुविधा पा सकते हैं. ये वेब वर्कर 2007 से लेकर अब तक काम कर रहे हैं. साल 2012 से अब तक सभी मुख्य ब्राउज़र में इनका इस्तेमाल हो रहा है. वेब वर्कर, मुख्य थ्रेड के साथ-साथ चलते हैं, लेकिन ओएस थ्रेडिंग के उलट, वे वैरिएबल शेयर नहीं कर सकते.

वेब वर्कर बनाने के लिए, वर्कर कंस्ट्रक्टर को कोई फ़ाइल पास करें. इससे, उस फ़ाइल को किसी अलग थ्रेड में चलाया जाएगा:

const worker = new Worker("./worker.js");

postMessage API से मैसेज भेजकर, वेब वर्कर से संपर्क करें. postMessage कॉल में मैसेज वैल्यू को पैरामीटर के तौर पर पास करें और फिर वर्कर के लिए मैसेज इवेंट लिसनर जोड़ें:

main.js

const worker = new Worker('./worker.js');
worker.postMessage([40, 2]);

worker.js

addEventListener('message', event => {
  const [a, b] = event.data;

  // Do stuff with the message
  // ...
});

मुख्य थ्रेड पर मैसेज भेजने के लिए, वेब वर्कर में उसी postMessage API का इस्तेमाल करें. साथ ही, मुख्य थ्रेड पर इवेंट लिसनर सेट अप करें:

main.js

const worker = new Worker('./worker.js');

worker.postMessage([40, 2]);
worker.addEventListener('message', event => {
  console.log(event.data);
});

worker.js

addEventListener('message', event => {
  const [a, b] = event.data;

  // Do stuff with the message
  postMessage(a + b);
});

सच कहूँ, तो यह तरीका सीमित है. अब तक, वेब वर्कर का इस्तेमाल मुख्य रूप से किसी एक भारी काम को मुख्य थ्रेड से निकालने के लिए किया जाता रहा है. एक वेब वर्कर के साथ कई कार्रवाइयां मैनेज करने की कोशिश बहुत जल्दी करनी पड़ती है: आपको न सिर्फ़ पैरामीटर, बल्कि मैसेज में मौजूद कार्रवाई को भी कोड में बदलना होगा. साथ ही, आपको अनुरोधों के जवाबों का मिलान करने के लिए हिसाब-किताब रखना पड़ता है. इस मुश्किल की वजह से ही वेब वर्कर को बड़े पैमाने पर अपनाया नहीं गया है.

हालांकि, अगर हम मुख्य थ्रेड और वेब वर्कर के बीच कम्यूनिकेट करने में आने वाली दिक्कतों को दूर कर सकें, तो यह मॉडल इस्तेमाल के कई मामलों के लिए सही साबित हो सकता है. अच्छी बात यह है कि यहां एक लाइब्रेरी है, जो इसका काम करती है!

Comlink एक लाइब्रेरी है. इसका लक्ष्य आपको postMessage की जानकारी का ध्यान रखे बिना वेब वर्कर का इस्तेमाल करने की सुविधा देना है. Comlink की मदद से, वेब वर्कर और मुख्य थ्रेड के बीच वैरिएबल शेयर किए जा सकते हैं. ये वैरिएबल, थ्रेडिंग के साथ काम करने वाली दूसरी प्रोग्रामिंग भाषाओं की तरह ही शेयर किए जा सकते हैं.

Comlink को किसी वेब वर्कर में इंपोर्ट करके और मुख्य थ्रेड को एक्सपोज़ करने के लिए फ़ंक्शन का सेट तय करके आपने Comlink को सेट अप किया है. इसके बाद, मुख्य थ्रेड पर Comlink इंपोर्ट करें, वर्कर को रैप करें, और बिना अनुमति के सार्वजनिक किए गए फ़ंक्शन का ऐक्सेस पाएं:

worker.js

import {expose} from 'comlink';

const api = {
  someMethod() {
    // ...
  }
}

expose(api);

main.js

import {wrap} from 'comlink';

const worker = new Worker('./worker.js');
const api = wrap(worker);

मुख्य थ्रेड पर api वैरिएबल, वेब वर्कर की तरह ही काम करता है. हालांकि, हर फ़ंक्शन, वैल्यू के बजाय वैल्यू के लिए प्रॉमिस लौटाता है.

आपको किस कोड को वेब वर्कर पर ले जाना चाहिए?

वेब वर्कर के पास DOM और WebUSB, WebRTC या वेब ऑडियो जैसे कई एपीआई का ऐक्सेस नहीं है. इसलिए, आप अपने ऐप्लिकेशन के उन हिस्सों को नहीं रख सकते जो वर्कर के लिए इस तरह के ऐक्सेस पर निर्भर होते हैं. फिर भी, कोड के हर छोटे हिस्से को वर्कर के पास ले जाने पर, मुख्य थ्रेड पर उन चीज़ों के लिए ज़्यादा हेडरूम खरीदे जाते हैं जो वहां मौजूद हैं, जैसे कि यूज़र इंटरफ़ेस अपडेट करना.

वेब डेवलपर के लिए एक समस्या यह है कि ज़्यादातर वेब ऐप्लिकेशन, ऐप्लिकेशन में मौजूद हर चीज़ को व्यवस्थित करने के लिए Vue या React जैसे यूज़र इंटरफ़ेस (यूआई) फ़्रेमवर्क का इस्तेमाल करते हैं. हर चीज़, फ़्रेमवर्क का हिस्सा है और इसलिए, यह स्वाभाविक रूप से DOM से जुड़ी होती है. ऐसा लगता है कि OMT आर्किटेक्चर पर माइग्रेट करना मुश्किल हो सकता है.

हालांकि, अगर हम ऐसे मॉडल पर स्विच करते हैं जिसमें यूज़र इंटरफ़ेस (यूआई) की समस्याएं अन्य समस्याओं से अलग हों, जैसे कि स्टेट मैनेजमेंट, तो फ़्रेमवर्क-आधारित ऐप्लिकेशन के साथ भी वेब वर्कर काफ़ी उपयोगी हो सकते हैं. PROXX के साथ यही तरीका अपनाया जाता है.

PROXX: OMT की केस स्टडी

Google Chrome की टीम ने PROXX को Minesweeper के क्लोन के तौर पर डेवलप किया है. यह प्रोग्रेसिव वेब ऐप्लिकेशन की ज़रूरी शर्तें पूरी करता है. इनमें ऑफ़लाइन काम करना और दिलचस्प उपयोगकर्ता अनुभव देना शामिल है. हालांकि, गेम के शुरुआती वर्शन, फ़ीचर फ़ोन जैसे सीमित डिवाइसों पर खराब परफ़ॉर्म करते थे. इससे टीम को लगा कि मुख्य थ्रेड एक बॉटलनेक है.

गेम की विज़ुअल स्थिति को लॉजिक से अलग रखने के लिए, टीम ने वेब वर्कर का इस्तेमाल करने का फ़ैसला किया:

  • मुख्य थ्रेड, ऐनिमेशन और ट्रांज़िशन की रेंडरिंग को हैंडल करता है.
  • कोई वेब वर्कर, गेम लॉजिक को हैंडल करता है, जो पूरी तरह से कंप्यूटेशनल होता है.

PROXX की फ़ीचर फ़ोन परफ़ॉर्मेंस पर OMT का असर दिलचस्प था. अन्य वर्शन में, यूज़र इंटरफ़ेस (यूआई) से उपयोगकर्ता के इंटरैक्ट करने के बाद छह सेकंड के लिए फ़्रीज़ किया जाता है. इसके लिए, उन्हें कोई सुझाव नहीं देना पड़ता. साथ ही, उपयोगकर्ता को कुछ और करने से पहले, पूरे छह सेकंड तक इंतज़ार करना पड़ता है.

PROXX के नॉन-OMT वर्शन में, यूज़र इंटरफ़ेस (यूआई) के लिए जवाब देने में लगने वाला समय.

हालांकि, OMT वर्शन में गेम को यूज़र इंटरफ़ेस (यूआई) अपडेट करने में बारह सेकंड लगते हैं. हालांकि ऐसा लगता है कि आपकी साइट की परफ़ॉर्मेंस में गिरावट आई है, लेकिन असल में इससे उपयोगकर्ता को ज़्यादा फ़ीडबैक मिलता है. ट्रैफ़िक धीमा तब होता है, जब ऐप्लिकेशन नॉन-OMT वर्शन के मुकाबले ज़्यादा फ़्रेम शिपिंग कर रहा होता है. इससे कोई फ़्रेम नहीं भेजा जाता. इसलिए, उपयोगकर्ता को पता होता है कि कुछ न कुछ गतिविधि हो रही है और वह यूज़र इंटरफ़ेस (यूआई) अपडेट के तौर पर गेम खेलना जारी रख सकता है. इससे, गेम काफ़ी बेहतर बन जाएगा.

PROXX के OMT वर्शन में, यूज़र इंटरफ़ेस (यूआई) के लिए जवाब देने में लगने वाला समय.

इसके लिए सोच-समझकर कदम उठाना होगा: हम सीमित डिवाइसों के उपयोगकर्ताओं को ऐसा अनुभव देते हैं जो महंगे डिवाइसों का इस्तेमाल करने वाले उपयोगकर्ताओं को कोई जुर्माना दिए बिना बेहतर महसूस होता है.

ओएमटी आर्किटेक्चर के असर

जैसा कि PROXX उदाहरण में दिखाया गया है, OMT आपके ऐप्लिकेशन को कई रेंज पर मौजूद डिवाइसों पर भरोसेमंद तरीके से चलाता है, लेकिन इससे आपका ऐप्लिकेशन ज़्यादा तेज़ नहीं होता:

  • आपको सिर्फ़ मुख्य थ्रेड से काम करने की ज़रूरत है, न कि काम को कम करने की.
  • वेब वर्कर और मुख्य थ्रेड के बीच अतिरिक्त कम्यूनिकेशन ओवरहेड की वजह से कभी-कभी चीज़ें थोड़ी धीमी हो सकती हैं.

उतार-चढ़ाव को ध्यान में रखते हुए

JavaScript के चलने के दौरान, मुख्य थ्रेड में लोगों के इंटरैक्शन बिना किसी शुल्क के प्रोसेस किए जा सकते हैं. जैसे, स्क्रोल करना. इसलिए, रेंडर होने में कम फ़्रेम आए हैं. हालांकि, इंतज़ार का कुल समय थोड़ा ज़्यादा लग सकता है. किसी फ़्रेम को छोड़ने के लिए, उपयोगकर्ता को थोड़ा इंतज़ार करना अच्छा विकल्प होता है, क्योंकि छोड़े गए फ़्रेम के लिए मार्जिन ऑफ़ एरर कम होता है: किसी फ़्रेम को छोड़ने की प्रक्रिया मिलीसेकंड में होती है, जबकि उपयोगकर्ता को इंतज़ार का समय समझने से पहले हज़ारों मिलीसेकंड होते हैं.

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

टूलिंग के बारे में एक नोट

वेब वर्कर अभी तक मुख्यधारा में नहीं हैं, इसलिए webpack और Rollup जैसे ज़्यादातर मॉड्यूल टूल अलग-अलग तरह से काम नहीं करते. (हालांकि, Parcel ने ऐसा किया है!) अच्छी बात यह है कि वेब वर्कर और रोलअप ऐप्लिकेशन का इस्तेमाल करने के लिए कुछ प्लगिन मौजूद हैं:

संक्षेप में

यह पक्का करने के लिए कि हमारे ऐप्लिकेशन ज़्यादा से ज़्यादा भरोसेमंद हों और उन्हें ऐक्सेस किया जा सके, खास तौर पर तेज़ी से बढ़ रहे ग्लोबल मार्केटप्लेस में, हमें सीमित डिवाइसों की ज़रूरत है—इनकी वजह से ही दुनिया भर में ज़्यादातर उपयोगकर्ता वेब को ऐक्सेस कर रहे हैं. OMT, महंगे डिवाइसों के उपयोगकर्ताओं पर बुरा असर डाले बिना, ऐसे डिवाइसों पर परफ़ॉर्मेंस को बेहतर बनाने का बेहतर तरीका देता है.

साथ ही, OMT में दूसरे फ़ायदे भी हैं:

वेब वर्कर को थोड़ा डराने-चौंकाने की ज़रूरत नहीं है. Comlink जैसे टूल, कर्मचारियों का काम आसान कर रहे हैं और उन्हें अलग-अलग वेब ऐप्लिकेशन के लिए बेहतर विकल्प बना रहे हैं.

Unsplash की हीरो इमेज, इसे जेम्स पीकॉक ने बनाया है.