लंबे टास्क को अलग-अलग करने के लिए, scheduler.yield() का इस्तेमाल करना

Brendan Kenny
Brendan Kenny

पब्लिश होने की तारीख: 6 मार्च, 2025

Browser Support

  • Chrome: 129.
  • Edge: 129.
  • Firefox: 142.
  • Safari: not supported.

Source

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

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

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

async function respondToUserClick() {
  giveImmediateFeedback();
  await scheduler.yield(); // Yield to the main thread.
  slowerComputation();
}

scheduler.yield का खास फ़ायदा यह है कि इससे पेज पर मौजूद अन्य टास्क को चलाने से पहले, पेज के लोड होने के बाद चलने वाले टास्क को शेड्यूल किया जा सकता है. यह नए टास्क शुरू करने के बजाय, किसी टास्क को जारी रखने को प्राथमिकता देता है.

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

उपज देने के बाद, प्राथमिकता के आधार पर जारी रखना

scheduler.yield, Prioritized Task Scheduling API का हिस्सा है. वेब डेवलपर के तौर पर, हम आम तौर पर इस बारे में बात नहीं करते कि इवेंट लूप, टास्क को किस क्रम में पूरा करता है. हालांकि, रिलेटिव प्राथमिकताएं हमेशा मौजूद रहती हैं. जैसे, setTimeout कॉल बैक के कतार में लगने के बाद requestIdleCallback कॉल बैक चलता है या ट्रिगर किया गया इनपुट इवेंट लिसनर आम तौर पर setTimeout(callback, 0) के साथ कतार में लगे टास्क से पहले चलता है.

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

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

यहां एक उदाहरण दिया गया है: दो फ़ंक्शन, जिन्हें setTimeout का इस्तेमाल करके अलग-अलग टास्क में चलाने के लिए लाइन में लगाया गया है.

setTimeout(myJob);
setTimeout(someoneElsesJob);

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

DevTools में यह काम ऐसा दिख सकता है:

Chrome DevTools के परफ़ॉर्मेंस पैनल में दो टास्क दिखाए गए हैं. दोनों को लंबे समय तक चलने वाले टास्क के तौर पर दिखाया गया है. पहले टास्क को पूरा करने में 'myJob' फ़ंक्शन का इस्तेमाल किया गया है, जबकि दूसरे टास्क को पूरा करने में 'someoneElsesJob' फ़ंक्शन का इस्तेमाल किया गया है.

myJob को लंबे समय तक चलने वाले टास्क के तौर पर फ़्लैग किया गया है. इसलिए, जब यह टास्क चल रहा होता है, तब ब्राउज़र कोई और काम नहीं कर पाता. अगर यह पहले-पक्ष की स्क्रिप्ट से है, तो हम इसे इस तरह से तोड़ सकते हैं:

function myJob() {
  // Run part 1.
  myJobPart1();
  // Yield with setTimeout() to break up long task, then run part2.
  setTimeout(myJobPart2, 0);
}

myJobPart2 को myJob के अंदर setTimeout के साथ चलाने के लिए शेड्यूल किया गया था. हालांकि, यह शेड्यूल बाद में लागू होगा, क्योंकि someoneElsesJob को पहले ही शेड्यूल किया जा चुका है. इसलिए, इस तरह से लागू किया जाएगा:

Chrome DevTools के परफ़ॉर्मेंस पैनल में तीन टास्क दिखाए गए हैं. पहला टास्क, फ़ंक्शन 'myJobPart1' को चला रहा है. दूसरा टास्क, 'someoneElsesJob' को चला रहा है. यह एक लंबा टास्क है. तीसरा टास्क, 'myJobPart2' को चला रहा है.

हमने टास्क को setTimeout के साथ अलग-अलग हिस्सों में बांटा है, ताकि ब्राउज़र myJob के बीच में रिस्पॉन्स दे सके. हालांकि, अब myJob का दूसरा हिस्सा सिर्फ़ someoneElsesJob के पूरा होने के बाद चलता है.

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

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

async function myJob() {
  // Run part 1.
  myJobPart1();
  // Yield with scheduler.yield() to break up long task, then run part2.
  await scheduler.yield();
  myJobPart2();
}

अब लागू करने पर ऐसा दिखेगा:

Chrome DevTools के परफ़ॉर्मेंस पैनल में दो टास्क दिखाए गए हैं. दोनों को लंबे समय तक चलने वाले टास्क के तौर पर दिखाया गया है. पहले टास्क को पूरा करने में 'myJob' फ़ंक्शन का इस्तेमाल किया गया है, जबकि दूसरे टास्क को पूरा करने में 'someoneElsesJob' फ़ंक्शन का इस्तेमाल किया गया है.

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

प्राथमिकता इनहेरिटेंस

Prioritized Task Scheduling API के तहत, scheduler.yield(), scheduler.postTask() में उपलब्ध प्राथमिकताओं के साथ अच्छी तरह से काम करता है. अगर प्राथमिकता साफ़ तौर पर सेट नहीं की गई है, तो scheduler.postTask() कॉलबैक के अंदर मौजूद scheduler.yield(), पिछले उदाहरण की तरह ही काम करेगा.

हालांकि, अगर प्राथमिकता सेट की जाती है, जैसे कि कम 'background' प्राथमिकता का इस्तेमाल करना:

async function lowPriorityJob() {
  part1();
  await scheduler.yield();
  part2();
}

scheduler.postTask(lowPriorityJob, {priority: 'background'});

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

इसका मतलब है कि अगर आपने कम प्राथमिकता वाले काम को 'background' scheduler.postTask() (या requestIdleCallback) के साथ शेड्यूल किया है, तो scheduler.yield() के बाद का काम तब तक नहीं होगा, जब तक ज़्यादातर टास्क पूरे नहीं हो जाते और मुख्य थ्रेड खाली नहीं हो जाती. कम प्राथमिकता वाले काम में आपको यही चाहिए.

एपीआई का इस्तेमाल कैसे करें

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

scheduler-polyfill, scheduler.postTask और scheduler.yield के लिए एक छोटा पॉलीफ़िल है. यह अंदरूनी तौर पर, कई तरीकों के कॉम्बिनेशन का इस्तेमाल करता है, ताकि दूसरे ब्राउज़र में शेड्यूलिंग एपीआई की कई सुविधाओं को इस्तेमाल किया जा सके. हालांकि, scheduler.yield() प्राथमिकता इनहेरिटेंस की सुविधा काम नहीं करती है.

अगर आपको पॉलीफ़िल से बचना है, तो setTimeout() का इस्तेमाल करके उपज प्राप्त की जा सकती है. साथ ही, प्राथमिकता के हिसाब से जारी रखने की सुविधा के नुकसान को स्वीकार किया जा सकता है. इसके अलावा, अगर यह स्वीकार्य नहीं है, तो उन ब्राउज़र में उपज प्राप्त न करें जिनमें यह सुविधा काम नहीं करती. ज़्यादा जानकारी के लिए, scheduler.yield()Optimize long tasks में मौजूद दस्तावेज़ देखें.

अगर आपको scheduler.yield() का पता लगाना है और फ़ॉलबैक खुद जोड़ना है, तो टाइप की जाँच करने और IDE की सहायता पाने के लिए, wicg-task-scheduling टाइप का भी इस्तेमाल किया जा सकता है.

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

एपीआई और यह टास्क की प्राथमिकताओं और scheduler.postTask() के साथ कैसे इंटरैक्ट करता है, इस बारे में ज़्यादा जानने के लिए MDN पर scheduler.yield() और प्राथमिकता के हिसाब से टास्क शेड्यूल करने की सुविधा से जुड़े दस्तावेज़ देखें.

लंबे समय तक चलने वाले टास्क, उपयोगकर्ता अनुभव को कैसे प्रभावित करते हैं, और उन्हें ठीक करने के तरीके के बारे में ज़्यादा जानने के लिए, लंबे समय तक चलने वाले टास्क को ऑप्टिमाइज़ करने के बारे में पढ़ें.