1. परिचय
इंटरैक्शन टू नेक्स्ट पेंट (आईएनपी) के बारे में जानने के लिए इंटरैक्टिव डेमो और कोडलैब.
ज़रूरी शर्तें
- एचटीएमएल और JavaScript डेवलपमेंट की जानकारी.
- सुझाव: INP से जुड़े दस्तावेज़ पढ़ें.
आपको ये सब सीखने को मिलेगा
- उपयोगकर्ता के इंटरैक्शन का इंटरैक्शन और उन इंटरैक्शन को हैंडल करने से, पेज के रिस्पॉन्सिव होने पर क्या असर पड़ता है.
- उपयोगकर्ताओं को बेहतर अनुभव देने के लिए, देरी को कम करने और हटाने का तरीका.
आपको इन चीज़ों की ज़रूरत पड़ेगी
- एक ऐसा कंप्यूटर जिसमें 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/ पर जाएं
ऐप्लिकेशन की खास जानकारी
यह पेज के सबसे ऊपर मौजूद होता है. यह स्कोर काउंटर और इंक्रीमेंटल बटन होता है. प्रतिक्रियाओं और रिस्पॉन्सिवनेस का क्लासिक डेमो!
बटन के नीचे चार माप होते हैं:
- आईएनपी: मौजूदा आईएनपी स्कोर, जो आम तौर पर सबसे खराब इंटरैक्शन होता है.
- इंटरैक्शन: सबसे हाल के इंटरैक्शन का स्कोर.
- FPS (फ़्रेम प्रति सेकंड): यह पेज के हर सेकंड के फ़्रेम के हिसाब से मुख्य थ्रेड होता है.
- टाइमर: जैंक को विज़ुअलाइज़ करने में मदद करने के लिए, चल रहा टाइमर ऐनिमेशन.
इंटरैक्शन को मापने के लिए, FPS और टाइमर की एंट्री ज़रूरी नहीं हैं. इन्हें बस इसलिए जोड़ा गया है, ताकि आप आसानी से विज़ुअलाइज़ कर सकें.
इसे आज़माएं
इंक्रीमेंटल बटन का इस्तेमाल करके इंटरैक्ट करने की कोशिश करें और स्कोर में हुई बढ़ोतरी देखें. क्या हर बढ़ोतरी के साथ INP और INP की वैल्यू बदलती हैं?
आईएनपी से यह पता चलता है कि उपयोगकर्ता के इंटरैक्ट करने के बाद से लेकर, पेज पर रेंडर किया गया अपडेट दिखाने में कितना समय लगता है.
3. Chrome DevTools की मदद से इंटरैक्शन को मेज़र करना
ज़्यादा टूल में जाकर DevTools खोलें > डेवलपर टूल मेन्यू में जाकर, पेज पर राइट क्लिक करके जांच करें को चुनें या कीबोर्ड शॉर्टकट का इस्तेमाल करें.
परफ़ॉर्मेंस पैनल पर स्विच करें. इसका इस्तेमाल, इंटरैक्शन को मेज़र करने के लिए किया जाएगा.
इसके बाद, परफ़ॉर्मेंस पैनल में इंटरैक्शन कैप्चर करें.
- 'रिकॉर्ड करें' दबाएं.
- पेज के साथ इंटरैक्ट करें (इंक्रीमेंटल बटन दबाएं).
- रिकॉर्डिंग बंद करें.
इससे बनने वाली टाइमलाइन में, आपको इंटरैक्शन ट्रैक दिखेगा. इसे बड़ा करने के लिए, बाईं ओर मौजूद त्रिभुज पर क्लिक करें.
दो इंटरैक्शन दिखाई देते हैं. दूसरे वाले पेज पर W बटन को दबाकर रखने या स्क्रोल करके ज़ूम इन करें.
इंटरैक्शन पर माउस घुमाने से, आप देख सकते हैं कि इंटरैक्शन तेज़ था और प्रोसेस होने में लगने वाला समय में कोई समय नहीं लगा था. साथ ही, इनपुट में देरी और प्रज़ेंटेशन में देरी में कम से कम समय लगा है. यह जांच कितनी देर तक हो सकती है, यह आपकी मशीन की स्पीड पर निर्भर करेगा.
4. लंबे समय तक चलने वाले इवेंट लिसनर
index.js
फ़ाइल खोलें और इवेंट लिसनर में blockFor
फ़ंक्शन की टिप्पणी हटाएं.
पूरा कोड देखें: click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
फ़ाइल सेव करें. सर्वर को बदलाव दिखेगा और वह आपके लिए पेज को रीफ़्रेश कर देगा.
पेज के साथ फिर से इंटरैक्ट करने की कोशिश करें. अब बातचीत काफ़ी धीमी हो जाएंगी.
परफ़ॉर्मेंस ट्रेस
परफ़ॉर्मेंस पैनल में एक और रिकॉर्डिंग करके देखें कि यह वहां कैसी दिखती है.
पहले जो छोटा सा इंटरैक्शन हुआ करता था, उसे अब शुरू होने में एक सेकंड लगता है.
इंटरैक्शन पर कर्सर घुमाकर, यह देखा जा सकता है कि "प्रोसेस करने की अवधि" में लगने वाला समय, करीब-करीब पूरा हो गया है. यह समय, इवेंट लिसनर के कॉलबैक को लागू करने में लगता है. blockFor
कॉल को ब्लॉक करने की वजह से, यह कॉल पूरी तरह इवेंट लिसनर के दायरे में आता है. इसलिए, ऐसा करने में ही समय लगता है.
5. एक्सपेरिमेंट: प्रोसेस होने की अवधि
इवेंट सुनने वाले लोगों के कॉन्टेंट को अलग तरीके से व्यवस्थित करके आईएनपी पर इसका असर देखें.
पहले यूज़र इंटरफ़ेस (यूआई) अपडेट करें
js कॉल का ऑर्डर बदलने पर क्या होता है—पहले यूज़र इंटरफ़ेस (यूआई) अपडेट करें और उसके बाद ब्लॉक करें?
पूरा कोड देखें: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
क्या आपने यूज़र इंटरफ़ेस (यूआई) को पहले देखा था? क्या इस ऑर्डर से आईएनपी स्कोर पर असर पड़ता है?
ट्रेस की जांच करें और इंटरैक्शन की जांच करके देखें कि कहीं कोई अंतर तो नहीं था.
अलग-अलग लिसनर
अगर आप अपने काम को एक अलग इवेंट लिसनर में ले जाएं, तो क्या होगा? यूज़र इंटरफ़ेस (यूआई) को एक इवेंट लिसनर में अपडेट करें और पेज को किसी दूसरे लिसनर के लिए ब्लॉक करें.
पूरा कोड देखें: 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. एक्सपेरिमेंट: इनपुट में देरी
क्या इवेंट लिसनर के अलावा, कोड को लंबे समय तक चलाने का क्या तरीका है? उदाहरण के लिए:
- अगर आपका
<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/कैनवस का इस्तेमाल करना
वेब पर आम तौर पर मिलने वाले कुछ उदाहरण:
- ऐसी SPA साइट जो किसी लिंक पर क्लिक करने के बाद, शुरुआती विज़ुअल फ़ीडबैक देने के लिए रोके बिना, पूरे DOM को फिर से बनाती है.
- ऐसा खोज पेज जो डाइनैमिक यूज़र इंटरफ़ेस वाले जटिल खोज फ़िल्टर उपलब्ध कराता है, लेकिन इसके लिए लिसनर का इस्तेमाल करना महंगा होता है.
- गहरे रंग वाला मोड, जो पूरे पेज के लिए स्टाइल/लेआउट को ट्रिगर करता है
10. एक्सपेरिमेंट: प्रज़ेंटेशन में देरी
requestAnimationFrame
ठीक से काम नहीं कर रहा है
requestAnimationFrame()
एपीआई का इस्तेमाल करके, चलिए यह बताते हैं कि प्रज़ेंटेशन में ज़्यादा समय लग सकता है या नहीं.
blockFor
कॉल को requestAnimationFrame
कॉलबैक में ले जाएं, ताकि इवेंट लिसनर के वापस आने के बाद चले:
पूरा कोड देखें: present_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
इस दिन क्या होगा?
11. प्रज़ेंटेशन में देरी से जुड़े एक्सपेरिमेंट के नतीजे
पूरा कोड देखें: present_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. प्रयोग: एक साथ काम नहीं करने वाली प्रोसेस
इंटरैक्शन में नॉन-विज़ुअल इफ़ेक्ट शुरू किए जा सकते हैं. जैसे, नेटवर्क के अनुरोध करना, टाइमर शुरू करना या ग्लोबल स्टेटस अपडेट करना. ऐसे में, उन आखिरकार से पेज को अपडेट करने पर क्या होगा?
जब तक किसी इंटरैक्शन के बाद अगले पेंट को रेंडर करने की अनुमति मिलती है, तब तक इंटरैक्शन मेज़रमेंट रुक जाता है. भले ही, ब्राउज़र यह तय करता हो कि उसे रेंडरिंग के किसी नए अपडेट की ज़रूरत नहीं है.
इसे आज़माने के लिए, क्लिक लिसनर से यूज़र इंटरफ़ेस (यूआई) अपडेट करना जारी रखें, लेकिन टाइम आउट से ब्लॉक करने का काम चलाएं.
पूरा कोड देखें: TIME_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
अब क्या होगा?
14. एक साथ काम नहीं करने वाली प्रोसेस से जुड़े प्रयोग के नतीजे
पूरा कोड देखें: TIME_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. एक से ज़्यादा इंटरैक्शन (और रेन क्लिक)
लंबे समय तक ब्लॉक करने के काम को इधर-उधर करने से मदद मिल सकती है, लेकिन उन लंबे टास्क से अब भी पेज ब्लॉक होता है. इससे आने वाले समय में होने वाले इंटरैक्शन पर असर पड़ता है. साथ ही, पेज के कई अन्य ऐनिमेशन और अपडेट पर असर पड़ता है.
पेज का एक साथ काम न करने वाला ब्लॉक करने वाला वर्शन फिर से आज़माएं (या अगर आपने आखिरी चरण में काम टालने के बारे में अपने हिसाब से विकल्प चुना था, तो वह वर्शन भी आज़माएं):
पूरा कोड देखें: TIME_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
अगर आप तेज़ी से कई बार क्लिक करते हैं, तो क्या होता है?
परफ़ॉर्मेंस ट्रेस
हर क्लिक के लिए, एक सेकंड का टास्क होता है. यह टास्क सूची में होता है. इससे यह पक्का किया जाता है कि मुख्य थ्रेड काफ़ी समय तक ब्लॉक रहे.
जब वे लंबे टास्क और नए क्लिक मिलने लगते हैं, तो इस वजह से इंटरैक्शन धीमा हो जाता है. भले ही, इवेंट लिसनर अपने-आप ही करीब-करीब तुरंत लौट आया हो. हमने इनपुट में देरी के साथ वाले पिछले एक्सपेरिमेंट की तरह ही स्थिति बनाई है. सिर्फ़ इस बार इनपुट में देरी setInterval
से नहीं, बल्कि पिछले इवेंट लिसनर की ओर से ट्रिगर किए गए काम से की गई है.
रणनीतियां
आम तौर पर, हम लंबे टास्क को पूरी तरह से हटाना चाहते हैं!
- गै़र-ज़रूरी कोड हटा दें, खास तौर पर स्क्रिप्ट.
- लंबे टास्क न चले, इसके लिए कोड को ऑप्टिमाइज़ करें.
- नए इंटरैक्शन आने पर पुराने काम रद्द करें.
16. पहली रणनीति: सोच-विचार करना
क्लासिक रणनीति. अगर एक के बाद एक लगातार इंटरैक्शन होते हैं और प्रोसेस या नेटवर्क का असर महंगा है, तो जान-बूझकर काम शुरू करने में देर करें, ताकि आप उसे रद्द करके रीस्टार्ट कर सकें. यह पैटर्न, ऑटोकंप्लीट फ़ील्ड जैसे यूज़र इंटरफ़ेस के लिए काम का है.
- महंगे काम को शुरू करने में देरी के लिए,
setTimeout
का इस्तेमाल करें. आम तौर पर, टाइमर का समय 500 से 1000 मिलीसेकंड तक होना चाहिए. - ऐसा करते समय टाइमर आईडी को सेव कर लें.
- अगर कोई नया इंटरैक्शन आता है, तो
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()
के साथ
हालांकि, हम आसानी से "यील्ड पॉइंट" जोड़ने के लिए, मॉडर्न async
और await
का इस्तेमाल कर सकते हैं को किसी भी JavaScript फ़ंक्शन से जोड़ा जा सकता है.
उदाहरण के लिए:
पूरा कोड देखें: extray.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 की मदद से, आईएनपी और इंटरैक्शन के ब्रेकडाउन को आसानी से मेज़र किया जा सकता है!
रणनीतियां
- आपके पेजों पर लंबे समय तक चलने वाला कोड (लंबे टास्क) न हो.
- ग़ैर-ज़रूरी कोड को इवेंट लिसनर से तब तक बाहर निकालें, जब तक अगली बार पेंट न किया जाए.
- पक्का करें कि रेंडरिंग अपडेट, ब्राउज़र के लिए अपने-आप में सही हो.