पेज के रिस्पॉन्स में लगने वाले समय (आईएनपी) को समझना

1. परिचय

पेज के रिस्पॉन्स में लगने वाला समय (आईएनपी) के बारे में जानने के लिए, इंटरैक्टिव डेमो और कोडलैब.

मुख्य थ्रेड पर इंटरैक्शन दिखाने वाला डायग्राम. उपयोगकर्ता, टास्क को ब्लॉक करते समय इनपुट देता है. इनपुट तब तक नहीं लिया जाता, जब तक ये टास्क पूरे नहीं हो जाते. इसके बाद, pointerup, mouseup, और click इवेंट लिसनर काम करते हैं. फिर, रेंडरिंग और पेंटिंग का काम शुरू हो जाता है. यह काम तब तक चलता है, जब तक अगला फ़्रेम नहीं दिख जाता

ज़रूरी शर्तें

आपको ये सब सीखने को मिलेगा

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

आपको इन चीज़ों की ज़रूरत पड़ेगी

  • ऐसा कंप्यूटर जिसमें GitHub से कोड क्लोन करने और npm कमांड चलाने की सुविधा हो.
  • टेक्स्ट एडिटर.
  • सभी इंटरैक्शन मेज़रमेंट के काम करने के लिए, Chrome का नया वर्शन.

2. सेट अप करें

कोड पाना और उसे लागू करना

यह कोड, web-vitals-codelabs रिपॉज़िटरी में मौजूद है.

  1. अपने टर्मिनल में रेपो को क्लोन करें: git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
  2. क्लोन की गई डायरेक्ट्री में जाएं: cd web-vitals-codelabs/understanding-inp
  3. डिपेंडेंसी इंस्टॉल करें: npm ci
  4. वेब सर्वर शुरू करें: npm run start
  5. अपने ब्राउज़र में http://localhost:5173/understanding-inp/ पर जाएं

ऐप्लिकेशन की खास जानकारी

पेज के सबसे ऊपर, स्कोर काउंटर और बढ़ाएं बटन मौजूद है. यह रिऐक्टिविटी और रिस्पॉन्सिवनेस का क्लासिक डेमो है!

इस कोडलैब के डेमो ऐप्लिकेशन का स्क्रीनशॉट

बटन के नीचे, चार मेज़रमेंट दिए गए हैं:

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

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

इसे आज़माएं

बढ़ाएं बटन पर क्लिक करके देखें कि स्कोर कैसे बढ़ता है. क्या हर बढ़ोतरी के साथ आईएनपी और इंटरैक्शन की वैल्यू बदलती हैं?

आईएनपी से यह पता चलता है कि उपयोगकर्ता के इंटरैक्ट करने से लेकर, पेज पर रेंडर किया गया अपडेट दिखने में कितना समय लगता है.

3. Chrome DevTools की मदद से इंटरैक्शन मेज़र करना

DevTools खोलें. इसके लिए, ज़्यादा टूल > डेवलपर टूल मेन्यू पर जाएं. इसके अलावा, पेज पर राइट क्लिक करके, जांच करें को चुनें या कीबोर्ड शॉर्टकट का इस्तेमाल करें.

परफ़ॉर्मेंस पैनल पर जाएं. इसका इस्तेमाल इंटरैक्शन मेज़र करने के लिए किया जाता है.

ऐप्लिकेशन के साथ-साथ DevTools के परफ़ॉर्मेंस पैनल का स्क्रीनशॉट

इसके बाद, परफ़ॉर्मेंस पैनल में इंटरैक्शन कैप्चर करें.

  1. 'रिकॉर्ड करें' दबाएं.
  2. पेज के साथ इंटरैक्ट करें (बढ़ाएं बटन दबाएं).
  3. रिकॉर्डिंग बंद करो.

इसके बाद, आपको टाइमलाइन में इंटरैक्शन ट्रैक दिखेगा. बाईं ओर मौजूद त्रिकोण पर क्लिक करके, इसे बड़ा करें.

DevTools के परफ़ॉर्मेंस पैनल का इस्तेमाल करके, इंटरैक्शन रिकॉर्ड करने का तरीका दिखाने वाला ऐनिमेशन

दो इंटरैक्शन दिखते हैं. स्क्रोल करके या W बटन को दबाकर, दूसरे फ़्रेम को बड़ा करें.

DevTools के परफ़ॉर्मेंस पैनल का स्क्रीनशॉट. इसमें कर्सर को पैनल में मौजूद इंटरैक्शन पर घुमाते हुए दिखाया गया है. साथ ही, टूलटिप में इंटरैक्शन का कम समय दिखाया गया है

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

4. लंबे समय तक चलने वाले इवेंट लिसनर

index.js फ़ाइल खोलें और इवेंट लिसनर में मौजूद blockFor फ़ंक्शन से टिप्पणी हटाएं.

पूरा कोड देखें: click_block.html

button.addEventListener('click', () => {
  blockFor(1000);
  score.incrementAndUpdateUI();
});

फ़ाइल सेव करें. सर्वर को बदलाव दिखेगा और वह आपके लिए पेज रीफ़्रेश करेगा.

पेज के साथ फिर से इंटरैक्ट करने की कोशिश करें. अब इंटरैक्शन काफ़ी धीमे हो जाएंगे.

परफ़ॉर्मेंस ट्रेस

परफ़ॉर्मेंस पैनल में एक और रिकॉर्डिंग लें, ताकि यह देखा जा सके कि यह कैसा दिखता है.

परफ़ॉर्मेंस पैनल में एक सेकंड तक का इंटरैक्शन

पहले यह इंटरैक्शन कुछ ही समय में पूरा हो जाता था, लेकिन अब इसमें एक सेकंड लगता है.

इंटरैक्शन पर कर्सर घुमाएं. ध्यान दें कि ज़्यादातर समय "प्रोसेसिंग में लगने वाला समय" में खर्च होता है. यह वह समय होता है जो इवेंट लिसनर कॉलबैक को लागू करने में लगता है. ब्लॉकिंग blockFor कॉल, इवेंट लिस्नर के अंदर होता है. इसलिए, समय वहीं लगता है.

5. एक्सपेरिमेंट: प्रोसेस होने की अवधि

इवेंट-लिसनर के काम को फिर से व्यवस्थित करने के तरीके आज़माएं. इससे INP पर पड़ने वाले असर को देखा जा सकता है.

पहले यूज़र इंटरफ़ेस (यूआई) को अपडेट करें

अगर JS कॉल का क्रम बदल दिया जाए, तो क्या होगा—पहले यूज़र इंटरफ़ेस (यूआई) अपडेट करें और फिर ब्लॉक करें?

पूरा कोड देखें: ui_first.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  blockFor(1000);
});

क्या आपने देखा कि यूज़र इंटरफ़ेस (यूआई) पहले दिख रहा था? क्या क्रम से INP स्कोर पर असर पड़ता है?

ट्रेस लेकर देखें और इंटरैक्शन की जांच करें. इससे पता चलेगा कि कोई अंतर है या नहीं.

अलग-अलग लिसनर

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

पूरा कोड देखें: two_click.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('click', () => {
  blockFor(1000);
});

अब यह परफ़ॉर्मेंस पैनल में कैसा दिखता है?

अलग-अलग तरह के इवेंट

ज़्यादातर इंटरैक्शन, कई तरह के इवेंट ट्रिगर करेंगे. जैसे, पॉइंटर या की इवेंट से लेकर होवर, फ़ोकस/ब्लर, और सिंथेटिक इवेंट, जैसे कि beforechange और beforeinput.

कई असली पेजों पर, अलग-अलग इवेंट के लिए लिसनर होते हैं.

अगर इवेंट लिसनर के लिए इवेंट टाइप बदल दिए जाएं, तो क्या होगा? उदाहरण के लिए, क्या click इवेंट लिसनर में से किसी एक को pointerup या mouseup से बदला जा सकता है?

पूरा कोड देखें: diff_handlers.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('pointerup', () => {
  blockFor(1000);
});

यूज़र इंटरफ़ेस (यूआई) में कोई बदलाव नहीं किया गया

अगर इवेंट लिसनर से यूज़र इंटरफ़ेस (यूआई) को अपडेट करने के लिए कॉल को हटा दिया जाता है, तो क्या होगा?

पूरा कोड देखें: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});

6. एक्सपेरिमेंट के नतीजों को प्रोसेस होने में लगने वाला समय

परफ़ॉर्मेंस ट्रेस: यूज़र इंटरफ़ेस (यूआई) को पहले अपडेट करें

पूरा कोड देखें: ui_first.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  blockFor(1000);
});

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

परफ़ॉर्मेंस पैनल में एक सेकंड तक चलने वाला इंटरैक्शन

परफ़ॉर्मेंस ट्रेस: अलग-अलग लिसनर

पूरा कोड देखें: two_click.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('click', () => {
  blockFor(1000);
});

हालांकि, फ़ंक्शन के हिसाब से दोनों में कोई अंतर नहीं है. इंटरैक्शन में अब भी एक सेकंड लगता है.

क्लिक इंटरैक्शन को ज़ूम इन करने पर, आपको दिखेगा कि click इवेंट के नतीजे के तौर पर, दो अलग-अलग फ़ंक्शन कॉल किए जा रहे हैं.

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

इस उदाहरण में, एक सेकंड के इंटरैक्शन को ज़ूम करके दिखाया गया है. इसमें दिखाया गया है कि पहले फ़ंक्शन कॉल को पूरा होने में एक मिलीसेकंड से भी कम समय लगता है

परफ़ॉर्मेंस ट्रेस: अलग-अलग तरह के इवेंट

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('pointerup', () => {
  blockFor(1000);
});

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

इस उदाहरण में, एक सेकंड के इंटरैक्शन को ज़ूम इन करके दिखाया गया है. इसमें दिखाया गया है कि pointerup लिसनर के बाद, क्लिक इवेंट लिसनर को पूरा होने में एक मिलीसेकंड से भी कम समय लगता है

परफ़ॉर्मेंस ट्रेस: यूज़र इंटरफ़ेस (यूआई) अपडेट नहीं किया गया

पूरा कोड देखें: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});
  • स्कोर अपडेट नहीं होता, लेकिन पेज अब भी अपडेट होता है!
  • ऐनिमेशन, सीएसएस इफ़ेक्ट, डिफ़ॉल्ट वेब कॉम्पोनेंट ऐक्शन (फ़ॉर्म इनपुट), टेक्स्ट एंट्री, और टेक्स्ट हाइलाइट करने की सुविधा अपडेट होती रहती है.

इस मामले में, बटन पर क्लिक करने पर वह चालू हो जाता है और फिर से बंद हो जाता है. इसके लिए, ब्राउज़र को पेंट करने की ज़रूरत होती है. इसका मतलब है कि अब भी आईएनपी मौजूद है.

इवेंट लिसनर ने मुख्य थ्रेड को एक सेकंड के लिए ब्लॉक कर दिया था. इससे पेज को पेंट होने से रोका गया. इसलिए, इंटरैक्शन में अब भी एक सेकंड लगता है.

परफ़ॉर्मेंस पैनल की रिकॉर्डिंग से पता चलता है कि इंटरैक्शन, पहले के इंटरैक्शन की तरह ही है.

परफ़ॉर्मेंस पैनल में एक सेकंड तक चलने वाला इंटरैक्शन

सीखने लायक अहम बातें

किसी भी इवेंट लिसनर में चलने वाला कोई भी कोड, इंटरैक्शन में देरी करेगा.

  • इसमें अलग-अलग स्क्रिप्ट और फ़्रेमवर्क से रजिस्टर किए गए लिसनर शामिल हैं. साथ ही, लिसनर में चलने वाला लाइब्रेरी कोड भी शामिल है. जैसे, स्टेट अपडेट, जो कॉम्पोनेंट रेंडर को ट्रिगर करता है.
  • सिर्फ़ आपका कोड ही नहीं, बल्कि तीसरे पक्ष की सभी स्क्रिप्ट भी.

यह एक सामान्य समस्या है!

आखिर में: सिर्फ़ इसलिए कि आपका कोड पेंट को ट्रिगर नहीं करता है, इसका मतलब यह नहीं है कि पेंट को पूरा करने के लिए, इवेंट लिसनर के धीमे होने का इंतज़ार नहीं किया जाएगा.

7. Experiment: input delay

इवेंट लिसनर के बाहर लंबे समय तक चलने वाले कोड के बारे में क्या? उदाहरण के लिए:

  • अगर आपने देर से लोड होने वाला <script> इस्तेमाल किया है, तो हो सकता है कि वह लोड होने के दौरान पेज को अचानक ब्लॉक कर दे.
  • क्या कोई एपीआई कॉल, जैसे कि setInterval, पेज को समय-समय पर ब्लॉक करता है?

इवेंट लिसनर से blockFor को हटाकर, इसे setInterval() में जोड़ें:

पूरा कोड देखें: input_delay.html

setInterval(() => {
  blockFor(1000);
}, 3000);


button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

इस दिन क्या होगा?

8. इनपुट में देरी से जुड़े एक्सपेरिमेंट के नतीजे

पूरा कोड देखें: input_delay.html

setInterval(() => {
  blockFor(1000);
}, 3000);


button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

setInterval ब्लॉकिंग टास्क के दौरान होने वाले बटन क्लिक को रिकॉर्ड करने से, लंबे समय तक चलने वाला इंटरैक्शन होता है. भले ही, इंटरैक्शन में कोई ब्लॉकिंग काम न किया जा रहा हो!

लंबे समय तक चलने वाले इन कामों को अक्सर लॉन्ग टास्क कहा जाता है.

DevTools में इंटरैक्शन पर कर्सर घुमाने पर, आपको दिखेगा कि इंटरैक्शन का समय अब मुख्य रूप से इनपुट में हुई देरी की वजह से है, न कि प्रोसेसिंग में लगने वाले समय की वजह से.

DevTools के परफ़ॉर्मेंस पैनल में एक सेकंड के लिए ब्लॉक करने वाला टास्क दिखाया गया है. साथ ही, उस टास्क के बीच में एक इंटरैक्शन और 642 मि॰से॰ का इंटरैक्शन दिखाया गया है. इसमें से ज़्यादातर समय इनपुट में देरी की वजह से लगा है

ध्यान दें कि इससे इंटरैक्शन पर हमेशा असर नहीं पड़ता! अगर टास्क के चालू होने पर क्लिक नहीं किया जाता है, तो हो सकता है कि आपको इनाम मिल जाए. इस तरह की "रैंडम" छींकों को डीबग करना मुश्किल हो सकता है, क्योंकि ये कभी-कभी ही समस्याएं पैदा करती हैं.

इनका पता लगाने के लिए, लंबे समय तक चलने वाले टास्क (या लंबे ऐनिमेशन फ़्रेम) और टोटल ब्लॉकिंग टाइम को मेज़र किया जा सकता है.

9. प्रज़ेंटेशन धीरे-धीरे दिख रहा है

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

ठीक है, पेज को महंगे इफ़ेक्ट के साथ अपडेट किया जा रहा है!

पेज अपडेट होने में भले ही कम समय लगे, लेकिन ब्राउज़र को उन्हें रेंडर करने में ज़्यादा समय लग सकता है!

मुख्य थ्रेड पर:

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

मुख्य थ्रेड से अलग:

  • जीपीयू इफ़ेक्ट को बेहतर बनाने के लिए सीएसएस का इस्तेमाल करना
  • बहुत बड़ी और हाई रिज़ॉल्यूशन वाली इमेज जोड़ना
  • जटिल सीन बनाने के लिए SVG/Canvas का इस्तेमाल करना

वेब पर रेंडरिंग के अलग-अलग एलिमेंट का स्केच

RenderingNG

वेब पर आम तौर पर दिखने वाले कुछ उदाहरण:

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

10. Experiment: presentation delay

requestAnimationFrame ठीक से काम नहीं कर रहा है

आइए, requestAnimationFrame() API का इस्तेमाल करके, लंबे समय तक प्रज़ेंटेशन में होने वाली देरी को सिम्युलेट करें.

blockFor कॉल को requestAnimationFrame कॉलबैक में ले जाएं, ताकि यह इवेंट लिसनर के वापस आने के बाद चले:

पूरा कोड देखें: presentation_delay.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  requestAnimationFrame(() => {
    blockFor(1000);
  });
});

इस दिन क्या होगा?

11. स्क्रीन पर दिखने में देरी से जुड़े एक्सपेरिमेंट के नतीजे

पूरा कोड देखें: presentation_delay.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  requestAnimationFrame(() => {
    blockFor(1000);
  });
});

इंटरैक्शन एक सेकंड तक चला, तो क्या हुआ?

requestAnimationFrame ने अगले पेंट से पहले कॉलबैक का अनुरोध किया है. आईएनपी, इंटरैक्शन से लेकर अगले पेंट तक के समय को मेज़र करता है. इसलिए, blockFor(1000) में मौजूद requestAnimationFrame अगले पेंट को एक सेकंड तक ब्लॉक करता रहता है.

परफ़ॉर्मेंस पैनल में एक सेकंड तक चलने वाला इंटरैक्शन

हालांकि, इन दो बातों पर ध्यान दें:

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

12. इंटरैक्शन का विश्लेषण करना

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

जब इंटरैक्शन लंबे समय तक चलते हैं, तो यह हमेशा साफ़ तौर पर पता नहीं चलता कि इसकी वजह क्या है. क्या इसकी वजह यह है:

  • इनपुट में देरी?
  • इवेंट प्रोसेस होने में कितना समय लगता है?
  • क्या प्रज़ेंटेशन में देरी हो रही है?

आपको जिस पेज पर भी रिस्पॉन्सिवनेस मेज़र करनी है उस पर DevTools का इस्तेमाल किया जा सकता है. इस आदत को अपनाने के लिए, यह तरीका आज़माएं:

  1. वेब पर उसी तरह से नेविगेट करें जैसे आम तौर पर किया जाता है.
  2. DevTools के परफ़ॉर्मेंस पैनल की लाइव मेट्रिक व्यू में, इंटरैक्शन लॉग पर नज़र रखें.
  3. अगर आपको किसी बातचीत का परफ़ॉर्मेंस खराब लगता है, तो उसे फिर से दोहराने की कोशिश करें:
  • अगर आपको यह सुविधा फिर से नहीं मिल रही है, तो इंटरैक्शन लॉग का इस्तेमाल करके अहम जानकारी पाएं.
  • अगर समस्या को दोहराया जा सकता है, तो परफ़ॉर्मेंस पैनल में ट्रेस रिकॉर्ड करें.

सभी देरी

पेज में इन सभी समस्याओं के बारे में थोड़ी-थोड़ी जानकारी जोड़ें:

पूरा कोड देखें: all_the_things.html

setInterval(() => {
  blockFor(1000);
}, 3000);

button.addEventListener('click', () => {
  blockFor(1000);
  score.incrementAndUpdateUI();

  requestAnimationFrame(() => {
    blockFor(1000);
  });
});

इसके बाद, समस्याओं का पता लगाने के लिए कंसोल और परफ़ॉर्मेंस पैनल का इस्तेमाल करें!

13. एक्सपेरिमेंट: एसिंक्रोनस तरीके से काम करना

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

जब तक इंटरैक्शन के बाद नेक्स्ट पेंट को रेंडर करने की अनुमति होती है, तब तक इंटरैक्शन मेज़रमेंट बंद हो जाता है. भले ही, ब्राउज़र यह तय करे कि उसे रेंडरिंग के नए अपडेट की ज़रूरत नहीं है.

इसे आज़माने के लिए, क्लिक लिसनर से यूज़र इंटरफ़ेस (यूआई) को अपडेट करना जारी रखें. हालांकि, टाइम आउट से ब्लॉकिंग का काम करें.

पूरा कोड देखें: timeout_100.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  setTimeout(() => {
    blockFor(1000);
  }, 100);
});

अब क्या होगा?

14. एसिंक्रोनस तरीके से काम करने से जुड़े एक्सपेरिमेंट के नतीजे

पूरा कोड देखें: timeout_100.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  setTimeout(() => {
    blockFor(1000);
  }, 100);
});

एक सेकंड लंबे टास्क के साथ 27 मिलीसेकंड का इंटरैक्शन, जो अब ट्रेस में बाद में हो रहा है

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

सबक: अगर आपको कोई आइटम हटाने का विकल्प नहीं मिलता है, तो कम से कम उसे दूसरी जगह ले जाएं!

तरीके

क्या हम 100 मिलीसेकंड setTimeout से बेहतर परफ़ॉर्म कर सकते हैं? हम अब भी चाहते हैं कि कोड जल्द से जल्द चले. अगर ऐसा नहीं होता, तो हमें इसे हटा देना चाहिए!

लक्ष्य:

  • इंटरैक्शन incrementAndUpdateUI() तक चलेगा.
  • blockFor() को जल्द से जल्द चलाया जाएगा, लेकिन इससे अगले पेंट को ब्लॉक नहीं किया जाएगा.
  • इससे "मैजिक टाइमआउट" के बिना, अनुमान के मुताबिक नतीजे मिलते हैं.

इसके लिए, ये तरीके अपनाए जा सकते हैं:

  • setTimeout(0)
  • Promise.then()
  • requestAnimationFrame
  • requestIdleCallback
  • scheduler.postTask()

"requestPostAnimationFrame"

requestAnimationFrame को अकेले इस्तेमाल करने पर, यह पहले अगले पेंट को चलाने की कोशिश करेगा. इससे इंटरैक्शन धीमा हो जाएगा. हालांकि, requestAnimationFrame + setTimeout का इस्तेमाल करने पर, requestPostAnimationFrame के लिए एक आसान पॉलीफ़िल तैयार हो जाता है. इससे अगले पेंट के बाद कॉलबैक चलता है.

पूरा कोड देखें: raf+task.html

function afterNextPaint(callback) {
  requestAnimationFrame(() => {
    setTimeout(callback, 0);
  });
}

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  afterNextPaint(() => {
    blockFor(1000);
  });
});

एर्गोनॉमिक्स के लिए, इसे प्रॉमिस में भी रैप किया जा सकता है:

पूरा कोड देखें: raf+task2.html

async function nextPaint() {
  return new Promise(resolve => afterNextPaint(resolve));
}

button.addEventListener('click', async () => {
  score.incrementAndUpdateUI();

  await nextPaint();
  blockFor(1000);
});

15. कई इंटरैक्शन (और तेज़ी से कई बार क्लिक करना)

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

पेज के एसिंक ब्लॉकिंग वर्क वर्शन को फिर से आज़माएं. अगर आपने पिछले चरण में काम को बाद के लिए टालने का अपना वैरिएशन बनाया है, तो उसे आज़माएं:

पूरा कोड देखें: timeout_100.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  setTimeout(() => {
    blockFor(1000);
  }, 100);
});

अगर इस बटन पर तेज़ी से कई बार क्लिक किया जाए, तो क्या होगा?

परफ़ॉर्मेंस ट्रेस

हर क्लिक के लिए, एक सेकंड का टास्क कतार में लगा दिया जाता है. इससे यह पक्का होता है कि मुख्य थ्रेड कुछ समय के लिए ब्लॉक हो गई है.

मुख्य थ्रेड में कई टास्क पूरे होने में दो सेकंड लग रहे हैं. इस वजह से, इंटरैक्शन में 800 मि॰से॰ तक का समय लग रहा है

जब लंबे समय तक चलने वाले टास्क, नए क्लिक के साथ ओवरलैप होते हैं, तो इंटरैक्शन धीमे हो जाते हैं. भले ही, इवेंट लिसनर तुरंत जवाब देता हो. हमने इनपुट में होने वाली देरी के साथ, पहले वाले एक्सपेरिमेंट जैसी ही स्थिति बनाई है. इस बार, इनपुट में देरी setInterval की वजह से नहीं हो रही है. हालांकि, यह देरी पहले के इवेंट लिसनर की वजह से ट्रिगर हुए काम की वजह से हो रही है.

रणनीतियां

हमारा मकसद, लंबे समय तक चलने वाले टास्क को पूरी तरह से हटाना है!

  • गैर-ज़रूरी कोड को पूरी तरह से हटाएं. खास तौर पर, स्क्रिप्ट को.
  • लंबे समय तक चलने वाले टास्क से बचने के लिए, कोड को ऑप्टिमाइज़ करें.
  • नए इंटरैक्शन आने पर, पुराने काम को बंद कर दें.

16. पहली रणनीति: डीबाउंस

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

  • ज़्यादा समय लेने वाले काम को शुरू करने में देरी करने के लिए, setTimeout का इस्तेमाल करें. इसके लिए, टाइमर का इस्तेमाल करें. टाइमर की अवधि 500 से 1,000 मिलीसेकंड हो सकती है.
  • ऐसा करते समय, टाइमर आईडी सेव करें.
  • अगर कोई नई बातचीत शुरू होती है, तो clearTimeout का इस्तेमाल करके पिछले टाइमर को रद्द करें.

पूरा कोड देखें: debounce.html

let timer;
button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  if (timer) {
    clearTimeout(timer);
  }
  timer = setTimeout(() => {
    blockFor(1000);
  }, 1000);
});

परफ़ॉर्मेंस ट्रेस

एक से ज़्यादा इंटरैक्शन, लेकिन उन सभी के नतीजे के तौर पर सिर्फ़ एक ज़्यादा समय लेने वाला काम

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

17. दूसरी रणनीति: लंबे समय तक चलने वाले काम में रुकावट डालना

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

अगर हमारे काम के बीच में कोई इंटरैक्शन आता है, तो हम चाहते हैं कि हमारा काम रुक जाए, ताकि नए इंटरैक्शन को तुरंत हैंडल किया जा सके. हम ऐसा कैसे कर सकते हैं?

isInputPending जैसे कुछ एपीआई उपलब्ध हैं. हालांकि, आम तौर पर लंबे टास्क को छोटे-छोटे हिस्सों में बांटना बेहतर होता है.

कई setTimeout

पहली कोशिश: कोई आसान काम करें.

पूरा कोड देखें: small_tasks.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();

  requestAnimationFrame(() => {
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
    setTimeout(() => blockFor(100), 0);
  });
});

यह सुविधा, ब्राउज़र को हर टास्क को अलग-अलग शेड्यूल करने की अनुमति देकर काम करती है. साथ ही, इनपुट को ज़्यादा प्राथमिकता दी जा सकती है!

कई इंटरैक्शन, लेकिन शेड्यूल किए गए सभी काम को कई छोटे-छोटे टास्क में बांट दिया गया है

हम पांच क्लिक के लिए पांच सेकंड के पूरे काम पर वापस आ गए हैं. हालांकि, हर क्लिक के लिए एक सेकंड के टास्क को 100 मिलीसेकंड के दस टास्क में बांट दिया गया है. इस वजह से, भले ही कई इंटरैक्शन उन टास्क के साथ ओवरलैप हो रहे हों, लेकिन किसी भी इंटरैक्शन में 100 मिलीसेकंड से ज़्यादा का इनपुट डिले नहीं है! ब्राउज़र, setTimeout के काम के मुकाबले, आने वाले इवेंट लिसनर को प्राथमिकता देता है. साथ ही, इंटरैक्शन तेज़ी से काम करते हैं.

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

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

अब yield() के साथ

हालांकि, हम किसी भी JavaScript फ़ंक्शन में आसानी से "yield points" जोड़ने के लिए, आधुनिक async और await का इस्तेमाल कर सकते हैं.

उदाहरण के लिए:

पूरा कोड देखें: yieldy.html

// Polyfill for scheduler.yield()
async function schedulerDotYield() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

async function blockInPiecesYieldy(ms) {
  const ms_per_part = 10;
  const parts = ms / ms_per_part;
  for (let i = 0; i < parts; i++) {
    await schedulerDotYield();

    blockFor(ms_per_part);
  }
}

button.addEventListener('click', async () => {
  score.incrementAndUpdateUI();
  await blockInPiecesYieldy(1000);
});

पहले की तरह, काम के एक हिस्से के बाद मुख्य थ्रेड को बंद कर दिया जाता है. इससे ब्राउज़र, आने वाले किसी भी इंटरैक्शन का जवाब दे पाता है. हालांकि, अब अलग-अलग setTimeout के बजाय सिर्फ़ एक await schedulerDotYield() की ज़रूरत होती है. इससे इसे for लूप के बीच में भी आसानी से इस्तेमाल किया जा सकता है.

अब AbortContoller() के साथ

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

डीबाउंसिंग की रणनीति का इस्तेमाल करके, हमने हर नए इंटरैक्शन के साथ पिछले टाइमआउट को रद्द कर दिया. क्या हम यहां भी ऐसा ही कुछ कर सकते हैं? इसके लिए, AbortController() का इस्तेमाल किया जा सकता है:

पूरा कोड देखें: aborty.html

// Polyfill for scheduler.yield()
async function schedulerDotYield() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

async function blockInPiecesYieldyAborty(ms, signal) {
  const parts = ms / 10;
  for (let i = 0; i < parts; i++) {
    // If AbortController has been asked to stop, abandon the current loop.
    if (signal.aborted) return;

    await schedulerDotYield();

    blockFor(10);
  }
}

let abortController = new AbortController();

button.addEventListener('click', async () => {
  score.incrementAndUpdateUI();

  abortController.abort();
  abortController = new AbortController();

  await blockInPiecesYieldyAborty(1000, abortController.signal);
});

जब कोई क्लिक होता है, तो blockInPiecesYieldyAborty for लूप शुरू हो जाता है. यह लूप, ज़रूरी काम करता है. साथ ही, समय-समय पर मुख्य थ्रेड को भी काम करने देता है, ताकि ब्राउज़र नए इंटरैक्शन के लिए रिस्पॉन्सिव बना रहे.

जब दूसरा क्लिक आता है, तो पहले लूप को AbortController के साथ रद्द किया गया के तौर पर फ़्लैग किया जाता है. साथ ही, एक नया blockInPiecesYieldyAborty लूप शुरू किया जाता है. जब पहली बार लूप को फिर से चलाने के लिए शेड्यूल किया जाता है, तो यह देखता है कि signal.aborted अब true है. इसके बाद, यह बिना कोई और काम किए तुरंत वापस आ जाता है.

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

18. नतीजा

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

याद रखें

  • आईएनपी, सभी इंटरैक्शन को मेज़र करता है.
  • हर इंटरैक्शन को इनपुट से लेकर नेक्स्ट पेंट तक मापा जाता है. इससे पता चलता है कि उपयोगकर्ता को रिस्पॉन्स मिलने में कितना समय लगा.
  • इनपुट में देरी, इवेंट को प्रोसेस करने में लगने वाला समय, और प्रज़ेंटेशन में देरी, ये सभी इंटरैक्शन की रिस्पॉन्सिवनेस पर असर डालते हैं.
  • DevTools की मदद से, आईएनपी और इंटरैक्शन ब्रेकडाउन को आसानी से मेज़र किया जा सकता है!

रणनीतियां

  • आपके पेजों पर लंबे समय तक चलने वाला कोड (लंबे टास्क) नहीं होना चाहिए.
  • अगले पेंट तक, इवेंट लिसनर से गैर-ज़रूरी कोड को हटा दें.
  • पक्का करें कि रेंडरिंग अपडेट, ब्राउज़र के लिए कारगर हो.

ज़्यादा जानें