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

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

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

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

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

वेबसाइट का अलग-अलग ब्राउज़र पर चलना

ब्राउज़र सहायता

  • 87
  • 87
  • x
  • x

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

बैकग्राउंड

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

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

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

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

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

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

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

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

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

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

हासिल करना हमेशा बुरा नहीं होता

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

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

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

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

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

  • is-input-Pending रिपॉज़िटरी में, स्पेसिफ़िकेशन के बारे में सुझाव/राय दें या शिकायत करें.
  • Twitter पर @acomminos (इस खास लेखक में से एक) से संपर्क करें.

नतीजा

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

Unsplash पर विल एच मैकमोहन की हीरो फ़ोटो.