कई साइटों और ऐप्लिकेशन में बहुत सारी स्क्रिप्ट होती हैं. आपको JavaScript को अक्सर जल्द से जल्द चालू कर लेना चाहिए, लेकिन हो सकता है कि आप न चाहते हों कि यह उपयोगकर्ता को कोई परेशानी मिले. अगर उपयोगकर्ता के पेज स्क्रोल करते समय Analytics का डेटा भेजा जाता है या बटन पर टैप करते समय एलिमेंट को डीओएम में जोड़ा जाता है, तो आपका वेब ऐप्लिकेशन काम नहीं कर सकता. इससे उपयोगकर्ता अनुभव खराब हो सकता है.
अच्छी खबर यह है कि अब एक एपीआई उपलब्ध है जो इससे आपको मदद मिल सकती है: requestIdleCallback
. जिस तरह requestAnimationFrame
को अपनाने से हमें ऐनिमेशन को ठीक से शेड्यूल करने और 60fps पर पहुंचने की संभावना बढ़ाने में मदद मिली, उसी तरह requestIdleCallback
फ़्रेम के खत्म होने पर खाली समय होने पर या उपयोगकर्ता के निष्क्रिय होने पर काम को शेड्यूल करेगा. इसका मतलब है कि उपयोगकर्ता की पसंद के बिना अपना काम किया जा सकता है. यह Chrome 47 में उपलब्ध है, इसलिए आप Chrome कैनरी का इस्तेमाल करके इसे आज ही आज़मा सकते हैं! यह एक्सपेरिमेंट के तौर पर शुरू की गई सुविधा है और इसकी खास जानकारी अब भी फ़्लो में है. इसलिए, आने वाले समय में चीज़ें बदल सकती हैं.
मुझे requestIdleCallback का इस्तेमाल क्यों करना चाहिए?
ग़ैर-ज़रूरी काम को खुद शेड्यूल करना बहुत मुश्किल है. यह पता लगाना नामुमकिन है कि कितना फ़्रेम टाइम बचा है, क्योंकि requestAnimationFrame
कॉलबैक के लागू होने के बाद, स्टाइल कैलकुलेशन, लेआउट, पेंट, और ब्राउज़र के अन्य एलिमेंट को चलाना पड़ता है. होम-रोल मोड में, इनमें से किसी भी सुविधा को शामिल नहीं किया जा सकता. यह पक्का करने के लिए कि कोई उपयोगकर्ता किसी तरह से इंटरैक्ट नहीं कर रहा है, आपको हर तरह के इंटरैक्शन (scroll
, touch
, click
) में लिसनर जोड़ने की ज़रूरत होगी. भले ही, आपको फ़ंक्शन के लिए उनकी ज़रूरत न हो, बस, ताकि आप यह पक्का कर सकें कि उपयोगकर्ता इंटरैक्ट नहीं कर रहा है. दूसरी ओर, ब्राउज़र यह जानता है कि फ़्रेम के आखिर में कितना समय उपलब्ध है और उपयोगकर्ता इंटरैक्ट कर रहा है या नहीं. इसी तरह, requestIdleCallback
के ज़रिए हमें एक एपीआई मिला. इसकी मदद से, हमें खाली समय का ज़्यादा से ज़्यादा इस्तेमाल करने में मदद मिली.
आइए, थोड़ा और विस्तार से इस पर गौर करें और जानते हैं कि हम इसका इस्तेमाल कैसे कर सकते हैं.
requestIdleCallback की जांच की जा रही है
यह requestIdleCallback
के लिए शुरुआती समय है. इसलिए, इसका इस्तेमाल करने से पहले देख लें कि यह इस्तेमाल के लिए उपलब्ध है या नहीं:
if ('requestIdleCallback' in window) {
// Use requestIdleCallback to schedule work.
} else {
// Do what you’d do today.
}
इसके व्यवहार में बदलाव भी किया जा सकता है. इसके लिए, setTimeout
पर वापस जाएं:
window.requestIdleCallback =
window.requestIdleCallback ||
function (cb) {
var start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
}
window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
}
setTimeout
का इस्तेमाल करना अच्छी बात नहीं है, क्योंकि यह requestIdleCallback
की तरह काम न करने के समय के बारे में नहीं जानता. हालांकि, requestIdleCallback
के उपलब्ध न होने पर आप फ़ंक्शन को सीधे कॉल करेंगे, इसलिए इस तरह से लगातार काम करना और भी आसान हो जाएगा. शिम के साथ, requestIdleCallback
उपलब्ध होने पर, आपके कॉल बिना किसी आवाज़ के रीडायरेक्ट हो जाएंगे. यह अच्छी बात है.
हालांकि, अभी के लिए मान लें कि यह मौजूद है.
requestIdleCallback का इस्तेमाल करना
requestIdleCallback
को कॉल करना, requestAnimationFrame
से काफ़ी मिलता-जुलता है. इसमें कॉलबैक फ़ंक्शन को अपने पहले पैरामीटर के तौर पर इस्तेमाल किया जाता है:
requestIdleCallback(myNonEssentialWork);
myNonEssentialWork
को कॉल करने पर, इसे एक deadline
ऑब्जेक्ट दिया जाएगा. इसमें एक ऐसा फ़ंक्शन होता है जो यह बताता है कि आपके काम में कितना समय बचा है:
function myNonEssentialWork (deadline) {
while (deadline.timeRemaining() > 0)
doWorkIfNeeded();
}
सबसे नई वैल्यू पाने के लिए, timeRemaining
फ़ंक्शन को कॉल किया जा सकता है. अगर timeRemaining()
की वैल्यू शून्य होने पर, आपको अब भी कुछ और काम करना है, तो दूसरा requestIdleCallback
शेड्यूल किया जा सकता है:
function myNonEssentialWork (deadline) {
while (deadline.timeRemaining() > 0 && tasks.length > 0)
doWorkIfNeeded();
if (tasks.length > 0)
requestIdleCallback(myNonEssentialWork);
}
फ़ंक्शन की गारंटी देना
अगर बहुत ज़्यादा भीड़ होती है, तो क्या करना चाहिए? आपको चिंता हो सकती है कि शायद आपके कॉलबैक पर कभी कॉल न मिले. हालांकि, requestIdleCallback
की वैल्यू requestAnimationFrame
से मिलती-जुलती है, लेकिन यह इस मामले में अलग है कि इसमें एक वैकल्पिक पैरामीटर की ज़रूरत होती है: एक विकल्प ऑब्जेक्ट, जिसमें टाइम आउट प्रॉपर्टी होती है. यह टाइम आउट सेट हो जाने पर, ब्राउज़र को मिलीसेकंड में समय देता है. इस समय तक उसे कॉलबैक करना ज़रूरी होता है:
// Wait at most two seconds before processing events.
requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
अगर टाइम आउट ट्रिगर होने की वजह से कॉलबैक किया जाता है, तो आपको दो चीज़ें दिखेंगी:
timeRemaining()
शून्य दिखाएगा.deadline
ऑब्जेक्ट कीdidTimeout
प्रॉपर्टी सही होगी.
अगर आपको पता चलता है कि didTimeout
सही है, तो हो सकता है कि आप सिर्फ़ काम चलाना चाहें और उसे पूरा करना चाहें:
function myNonEssentialWork (deadline) {
// Use any remaining time, or, if timed out, just run through the tasks.
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
tasks.length > 0)
doWorkIfNeeded();
if (tasks.length > 0)
requestIdleCallback(myNonEssentialWork);
}
इस टाइम आउट में आने वाली संभावित रुकावट की वजह से, आपके उपयोगकर्ताओं को यह पैरामीटर सेट करते समय सावधानी बरतनी पड़ सकती है. ऐसा होने पर, हो सकता है कि आपका ऐप्लिकेशन काम न करे या काम न करे. जहां हो सके, ब्राउज़र को यह तय करने दें कि कॉलबैक को कब कॉल करना है.
आंकड़े का डेटा भेजने के लिए requestIdleCallback का इस्तेमाल करना
आइए, आंकड़े भेजने के लिए requestIdleCallback
का इस्तेमाल करते हैं. इस मामले में, शायद हम किसी नेविगेशन मेन्यू पर टैप करके किसी इवेंट को ट्रैक करना चाहें, जैसे कि -- उदाहरण के लिए --. हालांकि, वे आम तौर पर स्क्रीन पर ऐनिमेट होते हैं, इसलिए हम इस इवेंट को तुरंत Google Analytics को नहीं भेजना चाहेंगे. हम भेजने और अनुरोध करने के लिए इवेंट का एक कलेक्शन बनाएंगे, ताकि उन्हें आने वाले समय में कभी भी भेजा जा सके:
var eventsToSend = [];
function onNavOpenClick () {
// Animate the menu.
menu.classList.add('open');
// Store the event for later.
eventsToSend.push(
{
category: 'button',
action: 'click',
label: 'nav',
value: 'open'
});
schedulePendingEvents();
}
अब हमें किसी भी बचे हुए इवेंट को प्रोसेस करने के लिए, requestIdleCallback
का इस्तेमाल करना होगा:
function schedulePendingEvents() {
// Only schedule the rIC if one has not already been set.
if (isRequestIdleCallbackScheduled)
return;
isRequestIdleCallbackScheduled = true;
if ('requestIdleCallback' in window) {
// Wait at most two seconds before processing events.
requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
} else {
processPendingAnalyticsEvents();
}
}
यहां देखा जा सकता है कि मैंने दो सेकंड का टाइम आउट सेट किया है, लेकिन यह वैल्यू आपके ऐप्लिकेशन पर निर्भर करेगी. आंकड़ों के डेटा के लिए, टाइम आउट का इस्तेमाल करना सही रहेगा. इससे यह पक्का किया जाता है कि डेटा की रिपोर्ट सही समयसीमा में की जाए, न कि आने वाले समय में.
आखिर में, हमें वह फ़ंक्शन लिखना होगा जिसे requestIdleCallback
एक्ज़ीक्यूट करेगा.
function processPendingAnalyticsEvents (deadline) {
// Reset the boolean so future rICs can be set.
isRequestIdleCallbackScheduled = false;
// If there is no deadline, just run as long as necessary.
// This will be the case if requestIdleCallback doesn’t exist.
if (typeof deadline === 'undefined')
deadline = { timeRemaining: function () { return Number.MAX_VALUE } };
// Go for as long as there is time remaining and work to do.
while (deadline.timeRemaining() > 0 && eventsToSend.length > 0) {
var evt = eventsToSend.pop();
ga('send', 'event',
evt.category,
evt.action,
evt.label,
evt.value);
}
// Check if there are more events still to send.
if (eventsToSend.length > 0)
schedulePendingEvents();
}
इस उदाहरण के लिए, मैंने यह मान लिया था कि अगर requestIdleCallback
मौजूद नहीं है, तो आंकड़ों का डेटा तुरंत भेजा जाना चाहिए. हालांकि, प्रोडक्शन ऐप्लिकेशन में बेहतर यही होगा कि मैसेज भेजने में देरी हो और इसके लिए टाइम आउट की वजह से कोई कार्रवाई न करें. इससे किसी भी इंटरैक्शन और जैंक की वजह से कोई गड़बड़ी नहीं होगी.
DOM में बदलाव करने के लिए requestIdleCallback का इस्तेमाल करना
एक और स्थिति, जहां requestIdleCallback
असल में परफ़ॉर्मेंस में मदद कर सकता है, जब आपको गैर-ज़रूरी डीओएम बदलाव करने हों. जैसे, हमेशा बढ़ती हुई और लेज़ी लोड की गई सूची के आखिर में आइटम जोड़ना. आइए देखते हैं कि requestIdleCallback
असल में किसी सामान्य फ़्रेम में कैसे फ़िट होता है.
ऐसा हो सकता है कि ब्राउज़र किसी दिए गए फ़्रेम में कॉलबैक चलाने के लिए बहुत व्यस्त हो. इसलिए, आपको यह उम्मीद नहीं करनी चाहिए कि फ़्रेम के आखिर में कोई खाली समय रहेगा. यह इसे setImmediate
जैसी चीज़ से अलग बनाता है, जो हर फ़्रेम के हिसाब से चलता है.
अगर कॉलबैक को फ़्रेम के आखिर में ट्रिगर किया जाता है, तो यह मौजूदा फ़्रेम के पूरा होने के बाद लागू होगा. इसका मतलब है कि स्टाइल में किए गए बदलाव लागू होंगे. साथ ही, लेआउट की गिनती की जाएगी. अगर हम इस्तेमाल में न होने वाले कॉलबैक में डीओएम बदलाव करते हैं, तो लेआउट कैलकुलेशन अमान्य हो जाएंगे. अगर अगले फ़्रेम में किसी भी तरह का लेआउट पढ़ा जा सकता है, जैसे getBoundingClientRect
, clientWidth
वगैरह के लिए ब्राउज़र को फ़ोर्स्ड सिंक्रोनस लेआउट करना होगा. यह परफ़ॉर्मेंस में आने वाली एक संभावित समस्या है.
इस्तेमाल न किए जा रहे कॉलबैक में DOM बदलावों को ट्रिगर न करने की एक और वजह यह है कि DOM बदलने के समय का अनुमान लगाना मुश्किल होता है. इससे हम ब्राउज़र में दी गई समयसीमा को आसानी से पार कर पाते हैं.
सबसे सही तरीका यह है कि requestAnimationFrame
कॉलबैक में सिर्फ़ डीओएम बदलाव किए जाएं, क्योंकि इन्हें ब्राउज़र इस तरह के काम को ध्यान में रखकर शेड्यूल करता है. इसका मतलब है कि हमारे कोड को दस्तावेज़ के एक फ़्रैगमेंट का इस्तेमाल करना होगा. इसके बाद, इसे अगले requestAnimationFrame
कॉलबैक में जोड़ा जा सकता है. अगर आपने वीडीओएम लाइब्रेरी का इस्तेमाल किया है, तो बदलाव करने के लिए requestIdleCallback
का इस्तेमाल करना होगा. हालांकि, आपको अगले requestAnimationFrame
कॉलबैक में डीओएम पैच को लागू करना होगा, न कि इस्तेमाल न किए जा रहे कॉलबैक पर.
इसलिए, आइए कोड पर एक नज़र डालें:
function processPendingElements (deadline) {
// If there is no deadline, just run as long as necessary.
if (typeof deadline === 'undefined')
deadline = { timeRemaining: function () { return Number.MAX_VALUE } };
if (!documentFragment)
documentFragment = document.createDocumentFragment();
// Go for as long as there is time remaining and work to do.
while (deadline.timeRemaining() > 0 && elementsToAdd.length > 0) {
// Create the element.
var elToAdd = elementsToAdd.pop();
var el = document.createElement(elToAdd.tag);
el.textContent = elToAdd.content;
// Add it to the fragment.
documentFragment.appendChild(el);
// Don't append to the document immediately, wait for the next
// requestAnimationFrame callback.
scheduleVisualUpdateIfNeeded();
}
// Check if there are more events still to send.
if (elementsToAdd.length > 0)
scheduleElementCreation();
}
यहां मैंने एलिमेंट बनाया और उसे पॉप्युलेट करने के लिए textContent
प्रॉपर्टी का इस्तेमाल किया. हालांकि, हो सकता है कि इसमें आपका एलिमेंट बनाने वाला कोड ज़्यादा शामिल हो! एलिमेंट scheduleVisualUpdateIfNeeded
बनाने के बाद, उसे कॉल किया जाता है. इससे एक requestAnimationFrame
कॉलबैक सेट अप हो जाता है, जो कि असल में दस्तावेज़ के फ़्रैगमेंट को मुख्य हिस्से में जोड़ेगा:
function scheduleVisualUpdateIfNeeded() {
if (isVisualUpdateScheduled)
return;
isVisualUpdateScheduled = true;
requestAnimationFrame(appendDocumentFragment);
}
function appendDocumentFragment() {
// Append the fragment and reset.
document.body.appendChild(documentFragment);
documentFragment = null;
}
वैसे तो सब ठीक है, अब डीओएम में आइटम जोड़ते समय हमें काफ़ी कम जैंक होने लगेंगे. शानदार!
अक्सर पूछे जाने वाले सवाल
- क्या पॉलीफ़िल मौजूद है?
अफ़सोस की बात नहीं है, लेकिन अगर आप
setTimeout
पर पारदर्शी तरीके से रीडायरेक्ट करना चाहते हैं, तो एक झंझट है. इस एपीआई के मौजूद होने की वजह यह है कि यह वेब प्लैटफ़ॉर्म के बीच के गैप को कम कर देता है. गतिविधि में कमी का अनुमान लगाना मुश्किल है, लेकिन फ़्रेम के आखिर में खाली समय का पता लगाने के लिए कोई JavaScript API मौजूद नहीं है, इसलिए सही अनुमान लगाना ज़रूरी है. काम को शेड्यूल करने के लिए,setTimeout
,setInterval
याsetImmediate
जैसे एपीआई का इस्तेमाल किया जा सकता है. हालांकि, ये उपयोगकर्ता इंटरैक्शन से बचने के लिए समय नहीं किए गए हैं, जैसे किrequestIdleCallback
की तरह. - अगर मैं समयसीमा खत्म कर दूं, तो क्या होगा?
अगर
timeRemaining()
, शून्य दिखाता है, लेकिन आपने लंबे समय तक चलने का विकल्प चुना है, तो ब्राउज़र की मदद से काम बंद होने के डर के बिना ऐसा किया जा सकता है. हालांकि, ब्राउज़र पर आपको अपनी वेबसाइट इस्तेमाल करने वाले लोगों को बेहतर अनुभव देने के लिए, समयसीमा दी जाती है. इसलिए, अगर वाकई में ऐसा करने की कोई ठोस वजह नहीं है, तो आपको हमेशा समयसीमा का पालन करना चाहिए. - क्या कोई ऐसा ज़्यादा से ज़्यादा मान है जो
timeRemaining()
के ज़रिए दिया जाएगा? हां, अभी तापमान 50 मि॰से॰ है. किसी रिस्पॉन्सिव ऐप्लिकेशन को बनाए रखने की कोशिश करते समय, उपयोगकर्ता के इंटरैक्शन के सभी जवाब 100 मि॰से॰ से कम होने चाहिए. अगर उपयोगकर्ता 50 मि॰से॰ की विंडो से इंटरैक्ट करता है, तो ज़्यादातर मामलों में उसे इस्तेमाल न होने वाले कॉलबैक को पूरा करने की अनुमति देनी चाहिए. साथ ही, ब्राउज़र को उपयोगकर्ता के इंटरैक्शन का जवाब देने के लिए भी अनुमति देनी चाहिए. अगर ब्राउज़र को लगता है कि उन्हें चलाने के लिए काफ़ी समय है, तो आपको एक के बाद एक कई काम नहीं करने वाले कॉलबैक मिल सकते हैं. - क्या ऐसा कोई काम है जो मुझे requestIdleCallback में नहीं करना चाहिए?
आम तौर पर, यह करना ज़रूरी है कि आप जो काम करें वह छोटे-छोटे हिस्सों (माइक्रोटास्क) में हो और इनके बारे में अनुमान लगाया जा सके. उदाहरण के लिए, खास तौर पर डीओएम में बदलाव करने पर, एक्ज़ीक्यूशन का समय अनुमान के मुताबिक नहीं होगा. ऐसा इसलिए, क्योंकि इससे स्टाइल कैलकुलेशन, लेआउट, पेंटिंग, और कंपोज़िटिंग को ट्रिगर किया जा सकता है. इसलिए, आपको
requestAnimationFrame
कॉलबैक में सिर्फ़ ऊपर सुझाए गए तरीके से डीओएम बदलाव करने चाहिए. इस बात का ध्यान रखें कि प्रॉमिस का समाधान या उन्हें अस्वीकार करना, क्योंकि काम न करने वाले कॉलबैक खत्म होने के तुरंत बाद कॉलबैक लागू हो जाते हैं, भले ही प्रॉमिस बाक़ी हो. - क्या मुझे फ़्रेम के आखिर में हमेशा
requestIdleCallback
मिलेगा? नहीं, हमेशा नहीं. फ़्रेम के खत्म होने पर खाली समय मिलने पर ब्राउज़र, कॉलबैक को शेड्यूल करेगा. इसके अलावा, अगर उपयोगकर्ता कोई गतिविधि नहीं करता है, तो ब्राउज़र उस समय कॉलबैक को शेड्यूल करेगा. आपको हर फ़्रेम के हिसाब से कॉलबैक की ज़रूरत नहीं होनी चाहिए. साथ ही, अगर आपको इसे तय समयसीमा में चलाना है, तो टाइम आउट का इस्तेमाल करें. - क्या मुझे एक से ज़्यादा
requestIdleCallback
कॉलबैक मिल सकते हैं? हां, काफ़ी हद तक आपके पास एक से ज़्यादाrequestAnimationFrame
कॉलबैक हो सकते हैं. हालांकि, यह याद रखना ज़रूरी है कि अगर आपका पहला कॉलबैक, इसके कॉलबैक के बचे हुए समय का इस्तेमाल कर लेता है, तो दूसरे कॉलबैक के लिए ज़्यादा समय नहीं बचेगा. इसके बाद, अन्य कॉलबैक को ब्राउज़र के कुछ समय तक इस्तेमाल न किए जाने तक इंतज़ार करना होगा. इसके बाद ही, उन्हें चलाया जा सकता है. आपको जो काम करना है उसके आधार पर, एक बार इस्तेमाल न होने वाले कॉलबैक का इस्तेमाल करना और काम को अलग-अलग हिस्सों में बांटना बेहतर हो सकता है. इसके अलावा, टाइम आउट का इस्तेमाल भी किया जा सकता है, ताकि यह पक्का किया जा सके कि किसी भी कॉलबैक में समय के लिए कोई समस्या न हो. - अगर मैं किसी दूसरे कॉलबैक के लिए, इस्तेमाल न होने वाला नया कॉलबैक सेट करूं, तो क्या होगा? इस्तेमाल में न होने वाले नए कॉलबैक को जल्द से जल्द चलाने के लिए शेड्यूल किया जाएगा. यह मौजूदा फ़्रेम के बजाय, अगले फ़्रेम से शुरू होगा.
कुछ समय से ऑनलाइन नहीं हैं!
requestIdleCallback
की मदद से कोड का इस्तेमाल किया जा सकता है, लेकिन इससे कोड को इस्तेमाल करने वालों को परेशानी नहीं होती. यह इस्तेमाल में आसान है और इसे इस्तेमाल किया जा सकता है. हालांकि, यह अभी शुरुआती दौर में है और ज़रूरी शर्तें पूरी तरह से ठीक नहीं हैं. इसलिए, आप हमें अपने सुझाव/राय दें या शिकायत करें.
इसे Chrome कैनरी में देखें, इसे अपने प्रोजेक्ट के लिए आज़माएं, और हमें बताएं कि आप कैसे आगे बढ़ते हैं!