इस कोडलैब (कोड बनाना सीखने के लिए ट्यूटोरियल) के बारे में जानकारी
1. परिचय
पेज के रिस्पॉन्स में लगने वाला समय (आईएनपी) के बारे में जानने के लिए, इंटरैक्टिव डेमो और कोडलैब.
ज़रूरी शर्तें
- एचटीएमएल और JavaScript डेवलपमेंट की जानकारी.
- सुझाया गया: आईएनपी से जुड़ा दस्तावेज़ पढ़ें.
आपको ये सब सीखने को मिलेगा
- उपयोगकर्ता के इंटरैक्शन और उन इंटरैक्शन को हैंडल करने के तरीके से, पेज के रिस्पॉन्सिव होने पर क्या असर पड़ता है.
- उपयोगकर्ता को बेहतर अनुभव देने के लिए, देरी को कैसे कम किया जाए और इसे कैसे खत्म किया जाए.
आपको इन चीज़ों की ज़रूरत पड़ेगी
- ऐसा कंप्यूटर जिसमें GitHub से कोड क्लोन करने और npm कमांड चलाने की सुविधा हो.
- टेक्स्ट एडिटर.
- सभी इंटरैक्शन मेज़रमेंट के काम करने के लिए, Chrome का नया वर्शन.
2. सेट अप करें
कोड पाना और उसे लागू करना
यह कोड, web-vitals-codelabs
रिपॉज़िटरी में मौजूद है.
- अपने टर्मिनल में रेपो को क्लोन करें:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
- क्लोन की गई डायरेक्ट्री में जाएं:
cd web-vitals-codelabs/understanding-inp
- डिपेंडेंसी इंस्टॉल करें:
npm ci
- वेब सर्वर शुरू करें:
npm run start
- अपने ब्राउज़र में http://localhost:5173/understanding-inp/ पर जाएं
ऐप्लिकेशन की खास जानकारी
पेज पर सबसे ऊपर, स्कोर काउंटर और बढ़ाएं बटन मौजूद होता है. यह रिएक्टिविटी और रिस्पॉन्सिवनेस का क्लासिक डेमो है!
बटन के नीचे, चार मेज़रमेंट दिए गए हैं:
- आईएनपी: मौजूदा आईएनपी स्कोर, जो आम तौर पर सबसे खराब इंटरैक्शन होता है.
- इंटरैक्शन: सबसे हाल के इंटरैक्शन का स्कोर.
- एफ़पीएस: पेज के मुख्य थ्रेड फ़्रेम-प्रति-सेकंड.
- टाइमर: यह एक टाइमर ऐनिमेशन है, जो जंक को विज़ुअलाइज़ करने में मदद करता है.
इंटरैक्शन को मेज़र करने के लिए, एफपीएस और टाइमर की एंट्री ज़रूरी नहीं हैं. इन्हें सिर्फ़ इसलिए जोड़ा गया है, ताकि रिस्पॉन्सिवनेस को विज़ुअलाइज़ करना थोड़ा आसान हो जाए.
इसे आज़माएं
बढ़ाएं बटन पर क्लिक करके देखें कि स्कोर कैसे बढ़ता है. क्या हर इंक्रीमेंट के साथ आईएनपी और इंटरैक्शन की वैल्यू बदलती हैं?
आईएनपी से पता चलता है कि उपयोगकर्ता के इंटरैक्ट करने से लेकर, पेज पर अपडेट रेंडर होने तक कितना समय लगता है.
3. Chrome DevTools की मदद से इंटरैक्शन मेज़र करना
DevTools कोज़्यादा टूल > डेवलपर टूल मेन्यू से खोलें. इसके लिए, पेज पर राइट क्लिक करके जांच करें को चुनें या कीबोर्ड शॉर्टकट का इस्तेमाल करें.
परफ़ॉर्मेंस पैनल पर जाएं. इसका इस्तेमाल इंटरैक्शन मेज़र करने के लिए किया जाता है.
इसके बाद, परफ़ॉर्मेंस पैनल में इंटरैक्शन कैप्चर करें.
- 'रिकॉर्ड करें' दबाएं.
- पेज के साथ इंटरैक्ट करें (बढ़ाएं बटन दबाएं).
- रिकॉर्डिंग बंद करो.
इसके बाद, आपको टाइमलाइन में इंटरैक्शन ट्रैक दिखेगा. बाईं ओर मौजूद त्रिकोण पर क्लिक करके इसे बड़ा करें.
दो इंटरैक्शन दिखते हैं. स्क्रोल करके या W बटन को दबाकर, दूसरे फ़्रेम को ज़ूम इन करें.
इंटरैक्शन पर कर्सर घुमाने से पता चलता है कि इंटरैक्शन तेज़ी से हुआ. इसमें प्रोसेसिंग में लगने वाला समय नहीं लगा. साथ ही, इनपुट में देरी और प्रेज़ेंटेशन में देरी में कम से कम समय लगा. इनकी सटीक अवधि, आपके डिवाइस की स्पीड पर निर्भर करेगी.
4. लंबे समय तक चलने वाले इवेंट लिसनर
index.js
फ़ाइल खोलें और इवेंट लिसनर में मौजूद blockFor
फ़ंक्शन से टिप्पणी हटाएं.
पूरा कोड देखें: click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
फ़ाइल सेव करें. सर्वर को बदलाव दिखेगा और वह आपके लिए पेज को रीफ़्रेश करेगा.
पेज के साथ फिर से इंटरैक्ट करके देखें. अब इंटरैक्शन काफ़ी धीमे हो जाएंगे.
परफ़ॉर्मेंस ट्रेस
परफ़ॉर्मेंस पैनल में एक और रिकॉर्डिंग लें, ताकि यह देखा जा सके कि यह वहां कैसा दिखता है.
पहले यह इंटरैक्शन कुछ समय के लिए होता था, लेकिन अब यह एक सेकंड तक होता है.
इंटरैक्शन पर कर्सर घुमाएं. ध्यान दें कि ज़्यादातर समय "प्रोसेसिंग की अवधि" में लगता है. यह वह समय होता है जो इवेंट लिसनर कॉलबैक को एक्ज़ीक्यूट करने में लगता है. ब्लॉक करने वाला blockFor
कॉल, इवेंट लिस्नर के अंदर होता है. इसलिए, समय वहीं लगता है.
5. Experiment: processing duration
इवेंट-लिसनर के काम को फिर से व्यवस्थित करने के तरीकों को आज़माएं. इससे INP पर पड़ने वाले असर को देखा जा सकता है.
पहले यूज़र इंटरफ़ेस (यूआई) को अपडेट करें
अगर JavaScript कॉल का क्रम बदल दिया जाए, तो क्या होगा? जैसे, पहले यूज़र इंटरफ़ेस (यूआई) को अपडेट करना और फिर ब्लॉक करना.
पूरा कोड देखें: 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
लिसनर के बाद चलता है.
परफ़ॉर्मेंस ट्रेस: यूज़र इंटरफ़ेस (यूआई) अपडेट नहीं किया गया
पूरा कोड देखें: 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 में इंटरैक्शन पर कर्सर घुमाने पर, आपको दिखेगा कि इंटरैक्शन का समय अब मुख्य रूप से इनपुट में हुई देरी की वजह से है, न कि प्रोसेसिंग में लगने वाले समय की वजह से.
ध्यान दें कि इससे इंटरैक्शन पर हमेशा असर नहीं पड़ता! अगर टास्क के चालू होने पर क्लिक नहीं किया जाता है, तो हो सकता है कि आपको इनाम मिल जाए. इस तरह की "रैंडम" छींकों को डीबग करना मुश्किल हो सकता है, क्योंकि इनसे कभी-कभी ही समस्याएं होती हैं.
इनका पता लगाने के लिए, लंबे समय तक चलने वाले टास्क (या लंबे ऐनिमेशन फ़्रेम) और टोटल ब्लॉकिंग टाइम को मेज़र किया जा सकता है.
9. प्रज़ेंटेशन धीरे-धीरे दिख रहा है
अब तक, हमने इनपुट डिले या इवेंट लिसनर के ज़रिए JavaScript की परफ़ॉर्मेंस देखी है. हालांकि, रेंडरिंग नेक्स्ट पेंट पर और किन चीज़ों का असर पड़ता है?
ठीक है, पेज को महंगे इफ़ेक्ट के साथ अपडेट किया जा रहा है!
पेज अपडेट होने में भले ही कम समय लगे, लेकिन ब्राउज़र को उन्हें रेंडर करने में ज़्यादा समय लग सकता है!
मुख्य थ्रेड पर:
- ऐसे यूज़र इंटरफ़ेस (यूआई) फ़्रेमवर्क जिन्हें स्थिति में बदलाव होने के बाद अपडेट रेंडर करने की ज़रूरत होती है
- डीओएम में बदलाव करने या कई महंगे सीएसएस क्वेरी सिलेक्टर को टॉगल करने से, स्टाइल, लेआउट, और पेंट से जुड़ी कई कार्रवाइयां ट्रिगर हो सकती हैं.
मुख्य थ्रेड से अलग:
- जीपीयू इफ़ेक्ट को बेहतर बनाने के लिए सीएसएस का इस्तेमाल करना
- बहुत बड़ी और हाई रिज़ॉल्यूशन वाली इमेज जोड़ना
- जटिल सीन बनाने के लिए SVG/Canvas का इस्तेमाल करना
वेब पर आम तौर पर मिलने वाले कुछ उदाहरण:
- एसपीए साइट, जिसमें किसी लिंक पर क्लिक करने के बाद पूरे DOM को फिर से बनाया जाता है. इसमें शुरुआती विज़ुअल फ़ीडबैक देने के लिए, कुछ समय नहीं लगता.
- यह एक ऐसा खोज पेज है जो डाइनैमिक यूज़र इंटरफ़ेस के साथ, खोज के लिए मुश्किल फ़िल्टर उपलब्ध कराता है. हालांकि, ऐसा करने के लिए यह महंगा लिसनर इस्तेमाल करता है.
- गहरे रंग वाले मोड को टॉगल करने की सुविधा, जो पूरे पेज के लिए स्टाइल/लेआउट को ट्रिगर करती है
10. Experiment: presentation delay
requestAnimationFrame
ठीक से काम नहीं कर रहा है
आइए, requestAnimationFrame()
एपीआई का इस्तेमाल करके, लंबे समय तक प्रज़ेंटेशन में होने वाली देरी को सिम्युलेट करें.
blockFor
कॉल को 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
ने अगले पेंट से पहले कॉलबैक का अनुरोध किया है. आईएनपी, इंटरैक्शन से लेकर अगले पेंट तक के समय को मेज़र करता है. इसलिए, requestAnimationFrame
में मौजूद blockFor(1000)
, अगले पेंट को एक सेकंड तक ब्लॉक करता रहता है.
हालांकि, इन दो बातों पर ध्यान दें:
- होवर करने पर, आपको दिखेगा कि अब इंटरैक्शन का पूरा समय "प्रज़ेंटेशन में देरी" में लग रहा है. ऐसा इसलिए है, क्योंकि इवेंट लिसनर के वापस आने के बाद, मुख्य थ्रेड ब्लॉक हो रही है.
- मुख्य थ्रेड की गतिविधि का रूट अब क्लिक इवेंट नहीं, बल्कि "ऐनिमेशन फ़्रेम फ़ायर्ड" है.
12. इंटरैक्शन का विश्लेषण करना
इस टेस्ट पेज पर, रिस्पॉन्सिवनेस को बहुत अच्छी तरह से दिखाया गया है. इसमें स्कोर, टाइमर, और काउंटर यूज़र इंटरफ़ेस (यूआई) शामिल हैं...हालांकि, सामान्य पेज की जांच करते समय यह ज़्यादा बेहतर तरीके से दिखता है.
जब इंटरैक्शन लंबे समय तक चलते हैं, तो हमेशा यह पता नहीं चलता कि इसकी वजह क्या है. क्या इसकी वजह यह है:
- इनपुट में देरी?
- इवेंट को प्रोसेस होने में कितना समय लगता है?
- क्या प्रज़ेंटेशन में देरी हो रही है?
आपको जिस पेज पर भी रिस्पॉन्सिवनेस मेज़र करनी है उस पर DevTools का इस्तेमाल किया जा सकता है. इसकी आदत डालने के लिए, यह तरीका आज़माएं:
- वेब को सामान्य तरीके से ब्राउज़ करें.
- DevTools के परफ़ॉर्मेंस पैनल के लाइव मेट्रिक व्यू में, इंटरैक्शन लॉग पर नज़र रखें.
- अगर आपको कोई इंटरैक्शन ठीक से काम नहीं करता हुआ दिखता है, तो उसे फिर से दोहराने की कोशिश करें:
- अगर आपको समस्या दोहराने में परेशानी आ रही है, तो जानकारी पाने के लिए इंटरैक्शन लॉग का इस्तेमाल करें.
- अगर समस्या को दोहराया जा सकता है, तो परफ़ॉर्मेंस पैनल में ट्रेस रिकॉर्ड करें.
सभी देरी
पेज में इन सभी समस्याओं के बारे में थोड़ी-थोड़ी जानकारी शामिल करें:
पूरा कोड देखें: 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);
});
यूज़र इंटरैक्शन अब छोटा हो गया है, क्योंकि यूज़र इंटरफ़ेस (यूआई) अपडेट होने के तुरंत बाद मुख्य थ्रेड उपलब्ध हो जाती है. लंबे समय तक चलने वाला ब्लॉकिंग टास्क अब भी चलता है. हालांकि, यह पेंटिंग के कुछ समय बाद चलता है, ताकि उपयोगकर्ता को यूज़र इंटरफ़ेस (यूआई) के बारे में तुरंत जानकारी मिल सके.
सबक: अगर आपको किसी आइटम को हटाने का विकल्प नहीं मिलता है, तो कम से कम उसे दूसरी जगह ले जाएं!
तरीके
क्या हम 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);
});
अगर इस बटन पर तेज़ी से कई बार क्लिक किया जाए, तो क्या होगा?
परफ़ॉर्मेंस ट्रेस
हर क्लिक के लिए, एक सेकंड का टास्क कतार में लगा दिया जाता है. इससे यह पक्का होता है कि मुख्य थ्रेड कुछ समय के लिए ब्लॉक हो गई है.
जब लंबे समय तक चलने वाले ये टास्क, नए क्लिक के साथ ओवरलैप होते हैं, तो इंटरैक्शन धीमे हो जाते हैं. भले ही, इवेंट लिसनर तुरंत जवाब देता हो. हमने इनपुट में होने वाली देरी के साथ, पहले वाले एक्सपेरिमेंट जैसी ही स्थिति बनाई है. इस बार, इनपुट में देरी 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 की मदद से, आईएनपी और इंटरैक्शन ब्रेकडाउन को आसानी से मेज़र किया जा सकता है!
रणनीतियां
- आपके पेजों पर लंबे समय तक चलने वाला कोड (लंबे टास्क) नहीं होना चाहिए.
- अगले पेंट तक, इवेंट लिसनर से गैर-ज़रूरी कोड को हटा दें.
- पक्का करें कि रेंडरिंग अपडेट, ब्राउज़र के लिए असरदार हो.