تاریخ انتشار: 6 مارس 2025
زمانی که کارهای طولانی موضوع اصلی را مشغول نگه میدارند، صفحه کند و بی پاسخ میشود و از انجام کارهای مهم دیگر مانند پاسخ دادن به ورودی کاربر جلوگیری میکند. در نتیجه، حتی کنترلهای فرم داخلی ممکن است برای کاربران شکسته به نظر برسند - گویی صفحه ثابت شده است - به اجزای سفارشی پیچیدهتر اهمیتی نمیدهید.
scheduler.yield()
راهی برای تسلیم شدن به رشته اصلی است – به مرورگر اجازه میدهد هر کار با اولویت بالا را اجرا کند – سپس اجرا را از جایی که متوقف شده ادامه میدهد. این یک صفحه را پاسخگوتر نگه می دارد و به نوبه خود به بهبود تعامل با رنگ بعدی (INP) کمک می کند.
scheduler.yield
یک API ارگونومیک ارائه می دهد که دقیقاً همان کاری را که می گوید انجام می دهد: اجرای تابعی که فراخوانی می شود در عبارت await scheduler.yield()
مکث می کند و به رشته اصلی تسلیم می شود و وظیفه را از بین می برد. اجرای بقیه تابع - که ادامه تابع نامیده می شود - در یک کار حلقه رویداد جدید برنامه ریزی می شود.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
مزیت خاص scheduler.yield
این است که ادامه پس از بازده برنامه ریزی شده قبل از اجرای هر کار مشابه دیگری که توسط صفحه در صف قرار گرفته است اجرا شود. ادامه یک کار را بر شروع کارهای جدید اولویت می دهد.
توابعی مانند setTimeout
یا scheduler.postTask
نیز میتوانند برای جدا کردن وظایف مورد استفاده قرار گیرند، اما این ادامهها معمولاً پس از هر کار جدید از قبل در صف اجرا میشوند و به طور بالقوه خطر تأخیرهای طولانی بین تسلیم شدن به رشته اصلی و تکمیل کار آنها را به همراه دارد.
تداوم بعد از تسلیم اولویت بندی شده است
scheduler.yield
بخشی از API زمانبندی وظایف اولویتدار است. به عنوان توسعهدهندگان وب، ما معمولاً در مورد ترتیبی که حلقه رویداد وظایف را بر اساس اولویتهای صریح اجرا میکند، صحبت نمیکنیم، اما اولویتهای نسبی همیشه وجود دارند ، مانند یک درخواست پاسخ به تماس requestIdleCallback
که پس از هر پاسخدهی setTimeout
در صف اجرا میشود، یا یک شنونده رویداد ورودی راهاندازی شده که معمولاً قبل از یک کار در صف قرار میگیرد، اجرا میشود setTimeout(callback, 0)
.
زمانبندی وظایف اولویتدار فقط این موضوع را واضحتر میکند، و تشخیص اینکه کدام کار قبل از دیگری اجرا میشود را آسانتر میکند و امکان تنظیم اولویتها برای تغییر ترتیب اجرا، در صورت نیاز را فراهم میکند.
همانطور که گفته شد، ادامه اجرای یک تابع پس از تسلیم شدن با scheduler.yield()
اولویت بیشتری نسبت به شروع کارهای دیگر دارد. مفهوم راهنما این است که ادامه یک کار باید ابتدا اجرا شود، قبل از اینکه به کارهای دیگر بروید. اگر کار کدی است که به خوبی رفتار میکند و بهطور دورهای نمایش داده میشود تا مرورگر بتواند کارهای مهم دیگری را انجام دهد (مانند پاسخ به ورودی کاربر)، نباید با اولویتبندی پس از سایر کارهای مشابه مجازات شود.
در اینجا یک مثال آورده شده است: دو تابع، در صف برای اجرا در وظایف مختلف با استفاده از setTimeout
.
setTimeout(myJob);
setTimeout(someoneElsesJob);
در این مورد، دو فراخوان setTimeout
دقیقاً در کنار یکدیگر قرار دارند، اما در یک صفحه واقعی، میتوانند در مکانهای کاملاً متفاوتی فراخوانی شوند، مانند یک اسکریپت شخص اول و یک اسکریپت شخص ثالث که به طور مستقل کار را برای اجرا تنظیم میکند، یا میتواند دو کار از مؤلفههای جداگانه باشد که در عمق زمانبندی فریمورک شما فعال میشوند.
این کار در DevTools چگونه می تواند باشد:
myJob
به عنوان یک کار طولانی علامت گذاری شده است و مرورگر را از انجام هر کار دیگری در حین اجرا مسدود می کند. با فرض اینکه از یک اسکریپت شخص اول است، می توانیم آن را تجزیه کنیم:
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
از آنجایی که myJobPart2
قرار بود با setTimeout
در myJob
اجرا شود، اما این زمانبندی پس از اینکه someoneElsesJob
قبلاً برنامهریزی شده بود اجرا میشود، نحوه اجرا به این صورت است:
ما کار را با setTimeout
تقسیم کردهایم تا مرورگر بتواند در اواسط myJob
پاسخگو باشد، اما اکنون بخش دوم myJob
فقط پس از پایان کار someoneElsesJob
اجرا میشود.
در برخی موارد، ممکن است خوب باشد، اما معمولاً این بهینه نیست. myJob
در حال تسلیم شدن به موضوع اصلی بود تا مطمئن شود که صفحه میتواند به ورودی کاربر پاسخگو باشد، نه اینکه موضوع اصلی را به طور کامل رها کند. در مواردی که someoneElsesJob
به خصوص کند است، یا بسیاری از کارهای دیگر غیر از someoneElsesJob
نیز برنامه ریزی شده اند، ممکن است زمان زیادی طول بکشد تا نیمه دوم myJob
اجرا شود. احتمالاً این هدف توسعهدهنده نبود که setTimeout
را به myJob
اضافه کرد.
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();
}
حالا اجرا به صورت زیر است:
مرورگر هنوز این فرصت را دارد که پاسخگو باشد، اما اکنون ادامه کار myJob
بر شروع کار جدید someoneElsesJob
اولویت دارد، بنابراین myJob
قبل از شروع someoneElsesJob
کامل می شود. این به انتظار تسلیم شدن به نخ اصلی برای حفظ پاسخگویی بسیار نزدیکتر است، نه اینکه موضوع اصلی را به طور کامل رها کنید.
وراثت اولویت دار
بهعنوان بخشی از API برنامهریزی وظایف اولویتدار بزرگتر، scheduler.yield()
به خوبی با اولویتهای صریح موجود در scheduler.postTask()
ترکیب میشود. بدون تعیین اولویت مشخص، یک scheduler.yield()
در یک callback scheduler.postTask()
اساساً مانند مثال قبلی عمل می کند.
با این حال، اگر اولویتی تنظیم شده باشد، مانند استفاده از اولویت 'background'
کم:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
ادامه با اولویتی بالاتر از سایر وظایف 'background'
برنامهریزی میشود – دریافت ادامه اولویتبندی شده مورد انتظار قبل از هر کار 'background'
معلق – اما همچنان اولویت کمتری نسبت به سایر وظایف پیشفرض یا با اولویت بالا است. کار 'background'
باقی می ماند.
این به این معنی است که اگر کار با اولویت پایین را با یک 'background'
scheduler.postTask()
(یا با requestIdleCallback
) برنامه ریزی کنید، ادامه بعد از scheduler.yield()
درون نیز منتظر می ماند تا اکثر وظایف دیگر کامل شوند و رشته اصلی برای اجرا بیکار باشد، که دقیقاً همان چیزی است که شما از بازدهی در یک کار با اولویت پایین می خواهید.
نحوه استفاده از API
در حال حاضر، scheduler.yield()
فقط در مرورگرهای مبتنی بر Chromium در دسترس است، بنابراین برای استفاده از آن باید ویژگیهای شناسایی و بازگشت به روش ثانویه بازدهی برای مرورگرهای دیگر را داشته باشید.
scheduler-polyfill
یک polyfill کوچک برای scheduler.postTask
و scheduler.yield
است که به صورت داخلی از ترکیبی از روشها برای شبیهسازی مقدار زیادی از قدرت APIهای زمانبندی در مرورگرهای دیگر استفاده میکند (اگرچه وراثت اولویت scheduler.yield()
پشتیبانی نمیشود).
برای کسانی که به دنبال اجتناب از polyfill هستند، یک روش این است که با استفاده از setTimeout()
تسلیم شوند و از دست دادن یک ادامه اولویتدار را بپذیرند، یا حتی در مرورگرهای پشتیبانینشده در صورتی که قابل قبول نیست، تسلیم نشوند. برای اطلاعات بیشتر، مستندات scheduler.yield()
را در بهینه سازی وظایف طولانی ببینید.
اگر میخواهید ویژگی scheduler.yield()
را شناسایی کنید و خودتان یک نسخه جایگزین اضافه کنید، میتوانید از انواع wicg-task-scheduling
برای بررسی نوع و پشتیبانی IDE نیز استفاده کنید.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد API و نحوه تعامل آن با اولویتهای کار و scheduler.postTask()
، اسناد scheduler.yield()
و اولویتبندی کارها را در MDN بررسی کنید.
برای کسب اطلاعات بیشتر در مورد وظایف طولانی، نحوه تأثیر آنها بر تجربه کاربر و کارهایی که باید در مورد آنها انجام دهید، درباره بهینه سازی کارهای طولانی بخوانید.
،تاریخ انتشار: 6 مارس 2025
زمانی که کارهای طولانی موضوع اصلی را مشغول نگه میدارند، صفحه کند و بی پاسخ میشود و از انجام کارهای مهم دیگر مانند پاسخ دادن به ورودی کاربر جلوگیری میکند. در نتیجه، حتی کنترلهای فرم داخلی ممکن است برای کاربران شکسته به نظر برسند - گویی صفحه ثابت شده است - به اجزای سفارشی پیچیدهتر اهمیتی نمیدهید.
scheduler.yield()
راهی برای تسلیم شدن به رشته اصلی است – به مرورگر اجازه میدهد هر کار با اولویت بالا را اجرا کند – سپس اجرا را از جایی که متوقف شده ادامه میدهد. این یک صفحه را پاسخگوتر نگه می دارد و به نوبه خود به بهبود تعامل با رنگ بعدی (INP) کمک می کند.
scheduler.yield
یک API ارگونومیک ارائه می دهد که دقیقاً همان کاری را که می گوید انجام می دهد: اجرای تابعی که فراخوانی می شود در عبارت await scheduler.yield()
مکث می کند و به رشته اصلی تسلیم می شود و وظیفه را از بین می برد. اجرای بقیه تابع - که ادامه تابع نامیده می شود - در یک کار حلقه رویداد جدید برنامه ریزی می شود.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
مزیت خاص scheduler.yield
این است که ادامه پس از بازده برنامه ریزی شده قبل از اجرای هر کار مشابه دیگری که توسط صفحه در صف قرار گرفته است اجرا شود. ادامه یک کار را بر شروع کارهای جدید اولویت می دهد.
توابعی مانند setTimeout
یا scheduler.postTask
نیز میتوانند برای جدا کردن وظایف مورد استفاده قرار گیرند، اما این ادامهها معمولاً پس از هر کار جدید از قبل در صف اجرا میشوند و به طور بالقوه خطر تأخیرهای طولانی بین تسلیم شدن به رشته اصلی و تکمیل کار آنها را به همراه دارد.
تداوم بعد از تسلیم اولویت بندی شده است
scheduler.yield
بخشی از API زمانبندی وظایف اولویتدار است. به عنوان توسعهدهندگان وب، ما معمولاً در مورد ترتیبی که حلقه رویداد وظایف را بر اساس اولویتهای صریح اجرا میکند، صحبت نمیکنیم، اما اولویتهای نسبی همیشه وجود دارند ، مانند یک درخواست پاسخ به تماس requestIdleCallback
که پس از هر پاسخدهی setTimeout
در صف اجرا میشود، یا یک شنونده رویداد ورودی راهاندازی شده که معمولاً قبل از یک کار در صف قرار میگیرد، اجرا میشود setTimeout(callback, 0)
.
زمانبندی وظایف اولویتدار فقط این موضوع را واضحتر میکند، و تشخیص اینکه کدام کار قبل از دیگری اجرا میشود را آسانتر میکند و امکان تنظیم اولویتها برای تغییر ترتیب اجرا، در صورت نیاز را فراهم میکند.
همانطور که گفته شد، ادامه اجرای یک تابع پس از تسلیم شدن با scheduler.yield()
اولویت بیشتری نسبت به شروع کارهای دیگر دارد. مفهوم راهنما این است که ادامه یک کار باید ابتدا اجرا شود، قبل از اینکه به کارهای دیگر بروید. اگر کار کدی است که به خوبی رفتار میکند و بهطور دورهای نمایش داده میشود تا مرورگر بتواند کارهای مهم دیگری را انجام دهد (مانند پاسخ به ورودی کاربر)، نباید با اولویتبندی پس از سایر کارهای مشابه مجازات شود.
در اینجا یک مثال آورده شده است: دو تابع، در صف برای اجرا در وظایف مختلف با استفاده از setTimeout
.
setTimeout(myJob);
setTimeout(someoneElsesJob);
در این مورد، دو فراخوان setTimeout
دقیقاً در کنار یکدیگر قرار دارند، اما در یک صفحه واقعی، میتوانند در مکانهای کاملاً متفاوتی فراخوانی شوند، مانند یک اسکریپت شخص اول و یک اسکریپت شخص ثالث که به طور مستقل کار را برای اجرا تنظیم میکند، یا میتواند دو کار از مؤلفههای جداگانه باشد که در عمق زمانبندی فریمورک شما فعال میشوند.
این کار در DevTools چگونه می تواند باشد:
myJob
به عنوان یک کار طولانی علامت گذاری شده است و مرورگر را از انجام هر کار دیگری در حین اجرا مسدود می کند. با فرض اینکه از یک اسکریپت شخص اول است، می توانیم آن را تجزیه کنیم:
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
از آنجایی که myJobPart2
قرار بود با setTimeout
در myJob
اجرا شود، اما این زمانبندی پس از اینکه someoneElsesJob
قبلاً برنامهریزی شده بود اجرا میشود، نحوه اجرا به این صورت است:
ما کار را با setTimeout
تقسیم کردهایم تا مرورگر بتواند در اواسط myJob
پاسخگو باشد، اما اکنون بخش دوم myJob
فقط پس از پایان کار someoneElsesJob
اجرا میشود.
در برخی موارد، ممکن است خوب باشد، اما معمولاً این بهینه نیست. myJob
در حال تسلیم شدن به موضوع اصلی بود تا مطمئن شود که صفحه میتواند به ورودی کاربر پاسخگو باشد، نه اینکه موضوع اصلی را به طور کامل رها کند. در مواردی که someoneElsesJob
به خصوص کند است، یا بسیاری از کارهای دیگر غیر از someoneElsesJob
نیز برنامه ریزی شده اند، ممکن است زمان زیادی طول بکشد تا نیمه دوم myJob
اجرا شود. احتمالاً این هدف توسعهدهنده نبود که setTimeout
را به myJob
اضافه کرد.
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();
}
حالا اجرا به صورت زیر است:
مرورگر هنوز این فرصت را دارد که پاسخگو باشد، اما اکنون ادامه کار myJob
بر شروع کار جدید someoneElsesJob
اولویت دارد، بنابراین myJob
قبل از شروع someoneElsesJob
کامل می شود. این به انتظار تسلیم شدن به نخ اصلی برای حفظ پاسخگویی بسیار نزدیکتر است، نه اینکه موضوع اصلی را به طور کامل رها کنید.
وراثت اولویت دار
بهعنوان بخشی از API برنامهریزی وظایف اولویتدار بزرگتر، scheduler.yield()
به خوبی با اولویتهای صریح موجود در scheduler.postTask()
ترکیب میشود. بدون تعیین اولویت مشخص، یک scheduler.yield()
در یک callback scheduler.postTask()
اساساً مانند مثال قبلی عمل می کند.
با این حال، اگر اولویتی تنظیم شده باشد، مانند استفاده از اولویت 'background'
کم:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
ادامه با اولویتی بالاتر از سایر وظایف 'background'
برنامهریزی میشود – دریافت ادامه اولویتبندی شده مورد انتظار قبل از هر کار 'background'
معلق – اما همچنان اولویت کمتری نسبت به سایر وظایف پیشفرض یا با اولویت بالا است. کار 'background'
باقی می ماند.
این به این معنی است که اگر کار با اولویت پایین را با یک 'background'
scheduler.postTask()
(یا با requestIdleCallback
) برنامه ریزی کنید، ادامه بعد از scheduler.yield()
درون نیز منتظر می ماند تا اکثر وظایف دیگر کامل شوند و رشته اصلی برای اجرا بیکار باشد، که دقیقاً همان چیزی است که شما از بازدهی در یک کار با اولویت پایین می خواهید.
نحوه استفاده از API
در حال حاضر، scheduler.yield()
فقط در مرورگرهای مبتنی بر Chromium در دسترس است، بنابراین برای استفاده از آن باید ویژگیهای شناسایی و بازگشت به روش ثانویه بازدهی برای مرورگرهای دیگر را داشته باشید.
scheduler-polyfill
یک polyfill کوچک برای scheduler.postTask
و scheduler.yield
است که به صورت داخلی از ترکیبی از روشها برای شبیهسازی مقدار زیادی از قدرت APIهای زمانبندی در مرورگرهای دیگر استفاده میکند (اگرچه وراثت اولویت scheduler.yield()
پشتیبانی نمیشود).
برای کسانی که به دنبال اجتناب از polyfill هستند، یک روش این است که با استفاده از setTimeout()
تسلیم شوند و از دست دادن یک ادامه اولویتدار را بپذیرند، یا حتی در مرورگرهای پشتیبانینشده در صورتی که قابل قبول نیست، تسلیم نشوند. برای اطلاعات بیشتر، مستندات scheduler.yield()
را در بهینه سازی وظایف طولانی ببینید.
اگر میخواهید ویژگی scheduler.yield()
را شناسایی کنید و خودتان یک نسخه جایگزین اضافه کنید، میتوانید از انواع wicg-task-scheduling
برای بررسی نوع و پشتیبانی IDE نیز استفاده کنید.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد API و نحوه تعامل آن با اولویتهای کار و scheduler.postTask()
، اسناد scheduler.yield()
و اولویتبندی کارها را در MDN بررسی کنید.
برای کسب اطلاعات بیشتر در مورد وظایف طولانی، نحوه تأثیر آنها بر تجربه کاربر و کارهایی که باید در مورد آنها انجام دهید، درباره بهینه سازی کارهای طولانی بخوانید.
،تاریخ انتشار: 6 مارس 2025
زمانی که کارهای طولانی موضوع اصلی را مشغول نگه میدارند، صفحه کند و بی پاسخ میشود و از انجام کارهای مهم دیگر مانند پاسخ دادن به ورودی کاربر جلوگیری میکند. در نتیجه، حتی کنترلهای فرم داخلی ممکن است برای کاربران شکسته به نظر برسند - گویی صفحه ثابت شده است - به اجزای سفارشی پیچیدهتر اهمیتی نمیدهید.
scheduler.yield()
راهی برای تسلیم شدن به رشته اصلی است – به مرورگر اجازه میدهد هر کار با اولویت بالا را اجرا کند – سپس اجرا را از جایی که متوقف شده ادامه میدهد. این یک صفحه را پاسخگوتر نگه می دارد و به نوبه خود به بهبود تعامل با رنگ بعدی (INP) کمک می کند.
scheduler.yield
یک API ارگونومیک ارائه می دهد که دقیقاً همان کاری را که می گوید انجام می دهد: اجرای تابعی که فراخوانی می شود در عبارت await scheduler.yield()
مکث می کند و به رشته اصلی تسلیم می شود و وظیفه را از بین می برد. اجرای بقیه تابع - که ادامه تابع نامیده می شود - در یک کار حلقه رویداد جدید برنامه ریزی می شود.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
مزیت خاص scheduler.yield
این است که ادامه پس از بازده برنامه ریزی شده قبل از اجرای هر کار مشابه دیگری که توسط صفحه در صف قرار گرفته است اجرا شود. ادامه یک کار را بر شروع کارهای جدید اولویت می دهد.
توابعی مانند setTimeout
یا scheduler.postTask
نیز میتوانند برای جدا کردن وظایف مورد استفاده قرار گیرند، اما این ادامهها معمولاً پس از هر کار جدید از قبل در صف اجرا میشوند و به طور بالقوه خطر تأخیرهای طولانی بین تسلیم شدن به رشته اصلی و تکمیل کار آنها را به همراه دارد.
تداوم بعد از تسلیم اولویت بندی شده است
scheduler.yield
بخشی از API زمانبندی وظایف اولویتدار است. به عنوان توسعهدهندگان وب، ما معمولاً در مورد ترتیبی که حلقه رویداد وظایف را بر اساس اولویتهای صریح اجرا میکند، صحبت نمیکنیم، اما اولویتهای نسبی همیشه وجود دارند ، مانند یک درخواست پاسخ به تماس requestIdleCallback
که پس از هر پاسخدهی setTimeout
در صف اجرا میشود، یا یک شنونده رویداد ورودی راهاندازی شده که معمولاً قبل از یک کار در صف قرار میگیرد، اجرا میشود setTimeout(callback, 0)
.
زمانبندی وظایف اولویتدار فقط این موضوع را واضحتر میکند، و تشخیص اینکه کدام کار قبل از دیگری اجرا میشود را آسانتر میکند و امکان تنظیم اولویتها برای تغییر ترتیب اجرا، در صورت نیاز را فراهم میکند.
همانطور که گفته شد، ادامه اجرای یک تابع پس از تسلیم شدن با scheduler.yield()
اولویت بیشتری نسبت به شروع کارهای دیگر دارد. مفهوم راهنما این است که ادامه یک کار باید ابتدا اجرا شود، قبل از اینکه به کارهای دیگر بروید. اگر کار کدی است که به خوبی رفتار میکند و بهطور دورهای نمایش داده میشود تا مرورگر بتواند کارهای مهم دیگری را انجام دهد (مانند پاسخ به ورودی کاربر)، نباید با اولویتبندی پس از سایر کارهای مشابه مجازات شود.
در اینجا یک مثال آورده شده است: دو تابع، در صف برای اجرا در وظایف مختلف با استفاده از setTimeout
.
setTimeout(myJob);
setTimeout(someoneElsesJob);
در این مورد، دو فراخوان setTimeout
دقیقاً در کنار یکدیگر قرار دارند، اما در یک صفحه واقعی، میتوانند در مکانهای کاملاً متفاوتی فراخوانی شوند، مانند یک اسکریپت شخص اول و یک اسکریپت شخص ثالث که به طور مستقل کار را برای اجرا تنظیم میکند، یا میتواند دو کار از مؤلفههای جداگانه باشد که در عمق زمانبندی فریمورک شما فعال میشوند.
این کار در DevTools چگونه می تواند باشد:
myJob
به عنوان یک کار طولانی علامت گذاری شده است و مرورگر را از انجام هر کار دیگری در حین اجرا مسدود می کند. با فرض اینکه از یک اسکریپت شخص اول است، می توانیم آن را تجزیه کنیم:
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
از آنجایی که myJobPart2
قرار بود با setTimeout
در myJob
اجرا شود، اما این زمانبندی پس از اینکه someoneElsesJob
قبلاً برنامهریزی شده بود اجرا میشود، نحوه اجرا به این صورت است:
ما کار را با setTimeout
تقسیم کردهایم تا مرورگر بتواند در اواسط myJob
پاسخگو باشد، اما اکنون بخش دوم myJob
فقط پس از پایان کار someoneElsesJob
اجرا میشود.
در برخی موارد، ممکن است خوب باشد، اما معمولاً این بهینه نیست. myJob
در حال تسلیم شدن به موضوع اصلی بود تا مطمئن شود که صفحه میتواند به ورودی کاربر پاسخگو باشد، نه اینکه موضوع اصلی را به طور کامل رها کند. در مواردی که someoneElsesJob
به خصوص کند است، یا بسیاری از کارهای دیگر غیر از someoneElsesJob
نیز برنامه ریزی شده اند، ممکن است زمان زیادی طول بکشد تا نیمه دوم myJob
اجرا شود. احتمالاً این هدف توسعهدهنده نبود که setTimeout
را به myJob
اضافه کرد.
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();
}
حالا اجرا به صورت زیر است:
مرورگر هنوز این فرصت را دارد که پاسخگو باشد، اما اکنون ادامه کار myJob
بر شروع کار جدید someoneElsesJob
اولویت دارد، بنابراین myJob
قبل از شروع someoneElsesJob
کامل می شود. این به انتظار تسلیم شدن به نخ اصلی برای حفظ پاسخگویی بسیار نزدیکتر است، نه اینکه موضوع اصلی را به طور کامل رها کنید.
وراثت اولویت دار
بهعنوان بخشی از API برنامهریزی وظایف اولویتدار بزرگتر، scheduler.yield()
به خوبی با اولویتهای صریح موجود در scheduler.postTask()
ترکیب میشود. بدون تعیین اولویت مشخص، یک scheduler.yield()
در یک callback scheduler.postTask()
اساساً مانند مثال قبلی عمل می کند.
با این حال، اگر اولویتی تنظیم شده باشد، مانند استفاده از اولویت 'background'
کم:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
ادامه با اولویتی بالاتر از سایر وظایف 'background'
برنامهریزی میشود – دریافت ادامه اولویتبندی شده مورد انتظار قبل از هر کار 'background'
معلق – اما همچنان اولویت کمتری نسبت به سایر وظایف پیشفرض یا با اولویت بالا است. کار 'background'
باقی می ماند.
این به این معنی است که اگر کار با اولویت پایین را با یک 'background'
scheduler.postTask()
(یا با requestIdleCallback
) برنامه ریزی کنید، ادامه بعد از scheduler.yield()
درون نیز منتظر می ماند تا اکثر وظایف دیگر کامل شوند و رشته اصلی برای اجرا بیکار باشد، که دقیقاً همان چیزی است که شما از بازدهی در یک کار با اولویت پایین می خواهید.
نحوه استفاده از API
در حال حاضر، scheduler.yield()
فقط در مرورگرهای مبتنی بر Chromium در دسترس است، بنابراین برای استفاده از آن باید ویژگیهای شناسایی و بازگشت به روش ثانویه بازدهی برای مرورگرهای دیگر را داشته باشید.
scheduler-polyfill
یک polyfill کوچک برای scheduler.postTask
و scheduler.yield
است که به صورت داخلی از ترکیبی از روشها برای شبیهسازی مقدار زیادی از قدرت APIهای زمانبندی در مرورگرهای دیگر استفاده میکند (اگرچه وراثت اولویت scheduler.yield()
پشتیبانی نمیشود).
برای کسانی که به دنبال اجتناب از polyfill هستند، یک روش این است که با استفاده از setTimeout()
تسلیم شوند و از دست دادن یک ادامه اولویتدار را بپذیرند، یا حتی در مرورگرهای پشتیبانینشده در صورتی که قابل قبول نیست، تسلیم نشوند. برای اطلاعات بیشتر، مستندات scheduler.yield()
را در بهینه سازی وظایف طولانی ببینید.
اگر میخواهید ویژگی scheduler.yield()
را شناسایی کنید و خودتان یک نسخه جایگزین اضافه کنید، میتوانید از انواع wicg-task-scheduling
برای بررسی نوع و پشتیبانی IDE نیز استفاده کنید.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد API و نحوه تعامل آن با اولویتهای کار و scheduler.postTask()
، اسناد scheduler.yield()
و اولویتبندی کارها را در MDN بررسی کنید.
برای کسب اطلاعات بیشتر در مورد وظایف طولانی، نحوه تأثیر آنها بر تجربه کاربر و کارهایی که باید در مورد آنها انجام دهید، درباره بهینه سازی کارهای طولانی بخوانید.
،تاریخ انتشار: 6 مارس 2025
زمانی که کارهای طولانی موضوع اصلی را مشغول نگه میدارند، صفحه کند و بی پاسخ میشود و از انجام کارهای مهم دیگر مانند پاسخ دادن به ورودی کاربر جلوگیری میکند. در نتیجه، حتی کنترلهای فرم داخلی ممکن است برای کاربران شکسته به نظر برسند - گویی صفحه ثابت شده است - به اجزای سفارشی پیچیدهتر اهمیتی نمیدهید.
scheduler.yield()
راهی برای تسلیم شدن به رشته اصلی است – به مرورگر اجازه میدهد هر کار با اولویت بالا را اجرا کند – سپس اجرا را از جایی که متوقف شده ادامه میدهد. این یک صفحه را پاسخگوتر نگه می دارد و به نوبه خود به بهبود تعامل با رنگ بعدی (INP) کمک می کند.
scheduler.yield
یک API ارگونومیک ارائه می دهد که دقیقاً همان کاری را که می گوید انجام می دهد: اجرای تابعی که فراخوانی می شود در عبارت await scheduler.yield()
مکث می کند و به رشته اصلی تسلیم می شود و وظیفه را از بین می برد. اجرای بقیه تابع - که ادامه تابع نامیده می شود - در یک کار حلقه رویداد جدید برنامه ریزی می شود.
async function respondToUserClick() {
giveImmediateFeedback();
await scheduler.yield(); // Yield to the main thread.
slowerComputation();
}
مزیت خاص scheduler.yield
این است که ادامه پس از بازده برنامه ریزی شده قبل از اجرای هر کار مشابه دیگری که توسط صفحه در صف قرار گرفته است اجرا شود. ادامه یک کار را بر شروع کارهای جدید اولویت می دهد.
توابعی مانند setTimeout
یا scheduler.postTask
نیز میتوانند برای جدا کردن وظایف مورد استفاده قرار گیرند، اما این ادامهها معمولاً پس از هر کار جدید از قبل در صف اجرا میشوند و به طور بالقوه خطر تأخیرهای طولانی بین تسلیم شدن به رشته اصلی و تکمیل کار آنها را به همراه دارد.
تداوم بعد از تسلیم اولویت بندی شده است
scheduler.yield
بخشی از API زمانبندی وظایف اولویتدار است. به عنوان توسعهدهندگان وب، ما معمولاً در مورد ترتیبی که حلقه رویداد وظایف را بر اساس اولویتهای صریح اجرا میکند، صحبت نمیکنیم، اما اولویتهای نسبی همیشه وجود دارند ، مانند یک درخواست پاسخ به تماس requestIdleCallback
که پس از هر پاسخدهی setTimeout
در صف اجرا میشود، یا یک شنونده رویداد ورودی راهاندازی شده که معمولاً قبل از یک کار در صف قرار میگیرد، اجرا میشود setTimeout(callback, 0)
.
زمانبندی وظایف اولویتدار فقط این موضوع را واضحتر میکند، و تشخیص اینکه کدام کار قبل از دیگری اجرا میشود را آسانتر میکند و امکان تنظیم اولویتها برای تغییر ترتیب اجرا، در صورت نیاز را فراهم میکند.
همانطور که گفته شد، ادامه اجرای یک تابع پس از تسلیم شدن با scheduler.yield()
اولویت بیشتری نسبت به شروع کارهای دیگر دارد. مفهوم راهنما این است که ادامه یک کار باید ابتدا اجرا شود، قبل از اینکه به کارهای دیگر بروید. اگر کار کدی است که به خوبی رفتار میکند و بهطور دورهای نمایش داده میشود تا مرورگر بتواند کارهای مهم دیگری را انجام دهد (مانند پاسخ به ورودی کاربر)، نباید با اولویتبندی پس از سایر کارهای مشابه مجازات شود.
در اینجا یک مثال آورده شده است: دو تابع، در صف برای اجرا در وظایف مختلف با استفاده از setTimeout
.
setTimeout(myJob);
setTimeout(someoneElsesJob);
در این مورد، دو فراخوان setTimeout
دقیقاً در کنار یکدیگر قرار دارند، اما در یک صفحه واقعی، میتوانند در مکانهای کاملاً متفاوتی فراخوانی شوند، مانند یک اسکریپت شخص اول و یک اسکریپت شخص ثالث که به طور مستقل کار را برای اجرا تنظیم میکند، یا میتواند دو کار از مؤلفههای جداگانه باشد که در عمق زمانبندی فریمورک شما فعال میشوند.
این کار در DevTools چگونه می تواند باشد:
myJob
به عنوان یک کار طولانی علامت گذاری شده است و مرورگر را از انجام هر کار دیگری در حین اجرا مسدود می کند. با فرض اینکه از یک اسکریپت شخص اول است، می توانیم آن را تجزیه کنیم:
function myJob() {
// Run part 1.
myJobPart1();
// Yield with setTimeout() to break up long task, then run part2.
setTimeout(myJobPart2, 0);
}
از آنجایی که myJobPart2
قرار بود با setTimeout
در myJob
اجرا شود، اما این زمانبندی پس از اینکه someoneElsesJob
قبلاً برنامهریزی شده بود اجرا میشود، نحوه اجرا به این صورت است:
ما کار را با setTimeout
تقسیم کردهایم تا مرورگر بتواند در اواسط myJob
پاسخگو باشد، اما اکنون بخش دوم myJob
فقط پس از پایان کار someoneElsesJob
اجرا میشود.
در برخی موارد، ممکن است خوب باشد، اما معمولاً این بهینه نیست. myJob
در حال تسلیم شدن به موضوع اصلی بود تا مطمئن شود که صفحه میتواند به ورودی کاربر پاسخگو باشد، نه اینکه موضوع اصلی را به طور کامل رها کند. در مواردی که someoneElsesJob
به خصوص کند است، یا بسیاری از کارهای دیگر غیر از someoneElsesJob
نیز برنامه ریزی شده اند، ممکن است زمان زیادی طول بکشد تا نیمه دوم myJob
اجرا شود. احتمالاً این هدف توسعهدهنده نبود که setTimeout
را به myJob
اضافه کرد.
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();
}
حالا اجرا به صورت زیر است:
مرورگر هنوز این فرصت را دارد که پاسخگو باشد، اما اکنون ادامه کار myJob
بر شروع کار جدید someoneElsesJob
اولویت دارد، بنابراین myJob
قبل از شروع someoneElsesJob
کامل می شود. این به انتظار تسلیم شدن به نخ اصلی برای حفظ پاسخگویی بسیار نزدیکتر است، نه اینکه موضوع اصلی را به طور کامل رها کنید.
وراثت اولویت دار
بهعنوان بخشی از API برنامهریزی وظایف اولویتدار بزرگتر، scheduler.yield()
به خوبی با اولویتهای صریح موجود در scheduler.postTask()
ترکیب میشود. بدون تعیین اولویت مشخص، یک scheduler.yield()
در یک callback scheduler.postTask()
اساساً مانند مثال قبلی عمل می کند.
با این حال، اگر اولویتی تنظیم شده باشد، مانند استفاده از اولویت 'background'
کم:
async function lowPriorityJob() {
part1();
await scheduler.yield();
part2();
}
scheduler.postTask(lowPriorityJob, {priority: 'background'});
ادامه با اولویتی بالاتر از سایر وظایف 'background'
برنامهریزی میشود – دریافت ادامه اولویتبندی شده مورد انتظار قبل از هر کار 'background'
معلق – اما همچنان اولویت کمتری نسبت به سایر وظایف پیشفرض یا با اولویت بالا است. کار 'background'
باقی می ماند.
این به این معنی است که اگر کار با اولویت پایین را با یک 'background'
scheduler.postTask()
(یا با requestIdleCallback
) برنامه ریزی کنید، ادامه بعد از scheduler.yield()
درون نیز منتظر می ماند تا اکثر وظایف دیگر کامل شوند و رشته اصلی برای اجرا بیکار باشد، که دقیقاً همان چیزی است که شما از بازدهی در یک کار با اولویت پایین می خواهید.
نحوه استفاده از API
در حال حاضر، scheduler.yield()
فقط در مرورگرهای مبتنی بر Chromium در دسترس است، بنابراین برای استفاده از آن باید ویژگیهای شناسایی و بازگشت به روش ثانویه بازدهی برای مرورگرهای دیگر را داشته باشید.
scheduler-polyfill
یک polyfill کوچک برای scheduler.postTask
و scheduler.yield
است که به صورت داخلی از ترکیبی از روشها برای شبیهسازی مقدار زیادی از قدرت APIهای زمانبندی در مرورگرهای دیگر استفاده میکند (اگرچه وراثت اولویت scheduler.yield()
پشتیبانی نمیشود).
برای کسانی که به دنبال اجتناب از polyfill هستند، یک روش این است که با استفاده از setTimeout()
تسلیم شوند و از دست دادن یک ادامه اولویتدار را بپذیرند، یا حتی در مرورگرهای پشتیبانینشده در صورتی که قابل قبول نیست، تسلیم نشوند. برای اطلاعات بیشتر، مستندات scheduler.yield()
را در بهینه سازی وظایف طولانی ببینید.
اگر میخواهید ویژگی scheduler.yield()
را شناسایی کنید و خودتان یک نسخه جایگزین اضافه کنید، میتوانید از انواع wicg-task-scheduling
برای بررسی نوع و پشتیبانی IDE نیز استفاده کنید.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد API و نحوه تعامل آن با اولویتهای کار و scheduler.postTask()
، اسناد scheduler.yield()
و اولویتبندی کارها را در MDN بررسی کنید.
برای کسب اطلاعات بیشتر در مورد وظایف طولانی، نحوه تأثیر آنها بر تجربه کاربر و کارهایی که باید در مورد آنها انجام دهید، درباره بهینه سازی کارهای طولانی بخوانید.