isInput Pending() के साथ बेहतर JS शेड्यूलिंग

एक नया JavaScript API, जिससे लोड करने की परफ़ॉर्मेंस और इनपुट रिस्पॉन्स के बीच के समझौते से बचा जा सकता है.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

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

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

ब्राउज़र के साथ काम करना

ब्राउज़र के इस्तेमाल से जुड़ी सहायता

  • Chrome: 87.
  • एज: 87.
  • Firefox: यह सुविधा काम नहीं करती.
  • Safari: यह सुविधा काम नहीं करती.

सोर्स

isInputPending() यह सुविधा, Chromium कोड वाले ब्राउज़र के वर्शन 87 से शुरू की गई है. किसी दूसरे ब्राउज़र ने एपीआई को शिप करने के इंटेंट का सिग्नल नहीं दिया है.

बैकग्राउंड

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

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

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

एक डायग्राम, जिसमें दिखाया गया है कि लंबे समय तक चलने वाले JS टास्क चलाने पर, ब्राउज़र के पास इवेंट डिस्पैच करने के लिए कम समय होता है.

Facebook पर, हम यह देखना चाहते थे कि अगर लोड करने के लिए हम कोई ऐसा नया तरीका अपनाते हैं जो इस समस्या को खत्म कर दे. इस बारे में हमने Chrome पर अपने दोस्तों से बात की और isInputPending() के लिए प्रस्ताव तैयार किया. isInputPending() एपीआई, वेब पर उपयोगकर्ता के इनपुट के लिए, इंटरप्ट के कॉन्सेप्ट का इस्तेमाल करने वाला पहला एपीआई है. साथ ही, यह JavaScript को ब्राउज़र के बिना इनपुट की जांच करने की अनुमति देता है.

इस डायग्राम में दिखाया गया है कि isInputPending() की मदद से, आपके JS को यह पता चलता है कि उपयोगकर्ता का कोई इनपुट बाकी है या नहीं. इसके लिए, ब्राउज़र को पूरी तरह से एक्सीक्यूशन नहीं दिया जाता.

एपीआई में लोगों की दिलचस्पी को देखते हुए, हमने Chromium में इस सुविधा को लागू करने और उसे उपलब्ध कराने के लिए, Chrome के अपने सहयोगियों के साथ साझेदारी की. Chrome के इंजीनियरों की मदद से, हमने ऑरिजिन ट्रायल के बाद पैच लागू किए. यह एक ऐसा तरीका है जिससे Chrome, एपीआई को पूरी तरह से रिलीज़ करने से पहले, बदलावों की जांच कर सकता है और डेवलपर से सुझाव/राय पा सकता है.

हमने ऑरिजिन ट्रायल और W3C वेब परफ़ॉर्मेंस वर्किंग ग्रुप के अन्य सदस्यों से सुझाव/राय ली है. साथ ही, एपीआई में बदलाव लागू कर दिए हैं.

उदाहरण: yieldier शेड्यूलर

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

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
 
if (performance.now() >= DEADLINE) {
   
// Yield the event loop if we're out of time.
    setTimeout
(processWorkQueue);
   
return;
 
}
  let job
= workQueue.shift();
  job
.execute();
}

setTimeout() की मदद से, नए मैक्रोटास्क में बाद में processWorkQueue() को शुरू करके, हम ब्राउज़र को इनपुट के लिए कुछ हद तक रिस्पॉन्सिव बनाते हैं. इससे, काम फिर से शुरू होने से पहले, ब्राउज़र इवेंट हैंडलर चला सकता है. साथ ही, यह बिना किसी रुकावट के काम करता रहता है. हालांकि, ऐसा हो सकता है कि इवेंट लूप को कंट्रोल करने का अनुरोध करने वाले किसी दूसरे काम की वजह से, इवेंट को लंबे समय के लिए रद्द कर दिया जाए या इवेंट के इंतज़ार में लगने वाले समय के लिए QUANTUM मिलीसेकंड तक बढ़ा दिया जाए.

यह ठीक है, लेकिन क्या हम इसे और बेहतर बना सकते हैं? बिल्कुल!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
 
if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
   
// Yield if we have to handle an input event, or we're out of time.
    setTimeout
(processWorkQueue);
   
return;
 
}
  let job
= workQueue.shift();
  job
.execute();
}

navigator.scheduling.isInputPending() पर कॉल करके, हम इनपुट का तुरंत जवाब दे सकते हैं. साथ ही, यह भी पक्का कर सकते हैं कि डिसप्ले ब्लॉक करने की सुविधा का काम बिना किसी रुकावट के जारी रहे. अगर काम पूरा होने तक, हमें इनपुट (जैसे, पेंटिंग) के अलावा कोई और चीज़ हैंडल करने में दिलचस्पी नहीं है, तो QUANTUM की अवधि भी बढ़ाई जा सकती है.

डिफ़ॉल्ट रूप से, isInputPending() से "लगातार होने वाले" इवेंट नहीं दिखाए जाते. इनमें mousemove, pointermove, और अन्य शामिल हैं. अगर आपको इनके लिए भी YIELD का इस्तेमाल करना है, तो कोई बात नहीं. isInputPending() को एक ऑब्जेक्ट देकर, includeContinuous को true पर सेट करें. इसके बाद, हम आगे बढ़ सकते हैं:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
 
if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
   
// Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout
(processWorkQueue);
   
return;
 
}
  let job
= workQueue.shift();
  job
.execute();
}

हो गया! React जैसे फ़्रेमवर्क, मिलते-जुलते लॉजिक का इस्तेमाल करके, अपनी मुख्य शेड्यूलिंग लाइब्रेरी में isInputPending() के साथ काम करने की सुविधा जोड़ रहे हैं. उम्मीद है कि इससे इन फ़्रेमवर्क का इस्तेमाल करने वाले डेवलपर, isInputPending() का फ़ायदा पा सकेंगे. इसके लिए, उन्हें कोड में काफ़ी बदलाव करने की ज़रूरत नहीं पड़ेगी.

हार मानना हमेशा बुरा नहीं होता

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

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

साथ ही, ऐसे पेजों को ध्यान में रखें जो किसी इवेंट लूप को शेयर करते हों. Android के लिए Chrome जैसे प्लैटफ़ॉर्म पर, एक से ज़्यादा ऑरिजिन के लिए किसी इवेंट लूप को शेयर करना आम बात है. अगर इनपुट को क्रॉस-ऑरिजिन फ़्रेम पर भेजा जाता है, तो isInputPending() कभी true नहीं दिखाएगा. इस वजह से, बैकग्राउंड वाले पेज की वजह से फ़ोरग्राउंड पेज के रिस्पॉन्स मिलने में रुकावट आ सकती है. बैकग्राउंड में काम करते समय Page visibility API का इस्तेमाल करके, काम को कम किया जा सकता है, टाला जा सकता है या फ़ायदा ज़्यादा हो सकता है.

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

सुझाव/राय दें या शिकायत करें

  • is-input-PENDING डेटा स्टोर करने की जगह में खास जानकारी के बारे में सुझाव/राय दें या शिकायत करें.
  • Twitter पर @acomminos (खास लेखकों में से एक) से संपर्क करें.

नतीजा

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

Unsplash पर Will H McMahan की हीरो फ़ोटो.