زمان‌بندی بهتر JS با isInputPending()، زمان‌بندی بهتر JS با isInputPending()

یک API جاوا اسکریپت جدید که ممکن است به شما کمک کند از تضاد بین عملکرد بار و پاسخگویی ورودی جلوگیری کنید.

نیت شلوس
Nate Schloss
اندرو کومینوس
Andrew Comminos

بارگیری سریع سخت است. سایت‌هایی که از JS برای ارائه محتوای خود استفاده می‌کنند، در حال حاضر باید بین عملکرد بارگذاری و پاسخ‌دهی ورودی معاوضه ایجاد کنند: یا تمام کارهای مورد نیاز برای نمایش را به یکباره انجام دهند (عملکرد بارگذاری بهتر، پاسخ‌گویی ورودی بدتر)، یا کار را به کوچک‌تر تقسیم کنند. وظایف به منظور پاسخگویی به ورودی و رنگ (عملکرد بدتر بار، پاسخ دهی بهتر ورودی).

برای از بین بردن نیاز به انجام این مبادله، فیس بوک API isInputPending() را در Chromium پیشنهاد و پیاده سازی کرد تا بتواند پاسخگویی را بدون تسلیم شدن بهبود بخشد. بر اساس بازخورد آزمایشی اصلی، ما تعدادی به‌روزرسانی برای API انجام داده‌ایم و خوشحالیم که اعلام کنیم API اکنون به‌طور پیش‌فرض در Chromium 87 ارسال می‌شود!

سازگاری با مرورگر

پشتیبانی مرورگر

  • کروم: 87.
  • لبه: 87.
  • فایرفاکس: پشتیبانی نمی شود.
  • سافاری: پشتیبانی نمی شود.

منبع

isInputPending() در مرورگرهای مبتنی بر Chromium از نسخه 87 ارسال شد. هیچ مرورگر دیگری قصد ارسال API را نشان نداده است.

پس زمینه

بیشتر کارها در اکوسیستم JS امروزی بر روی یک رشته انجام می شود: رشته اصلی. این یک مدل اجرای قوی برای توسعه دهندگان ارائه می دهد، اما تجربه کاربر (به ویژه پاسخگویی) در صورت اجرای طولانی مدت اسکریپت می تواند به شدت آسیب ببیند. به عنوان مثال، اگر صفحه در حال انجام کارهای زیادی در حین فعال شدن یک رویداد ورودی باشد، صفحه رویداد ورودی کلیک را تا زمانی که کار کامل نشده است کنترل نمی کند.

بهترین روش فعلی این است که با شکستن جاوا اسکریپت به بلوک های کوچکتر با این مشکل مقابله کنید. در حالی که صفحه در حال بارگیری است، صفحه می تواند کمی جاوا اسکریپت را اجرا کند و سپس کنترل را به مرورگر بازگرداند. سپس مرورگر می‌تواند صف رویداد ورودی خود را بررسی کند و ببیند آیا چیزی وجود دارد که باید به صفحه بگوید. سپس مرورگر می تواند به اجرای بلوک های جاوا اسکریپت با اضافه شدن آنها برگردد. این کمک می کند، اما می تواند باعث مشکلات دیگری شود.

هر بار که صفحه کنترل را به مرورگر باز می‌گرداند، مدتی طول می‌کشد تا مرورگر صف رویداد ورودی خود را بررسی کند، رویدادها را پردازش کند و بلوک بعدی جاوا اسکریپت را انتخاب کند. در حالی که مرورگر سریعتر به رویدادها پاسخ می دهد، زمان بارگذاری کلی صفحه کند می شود. و اگر به دفعات تسلیم شویم، صفحه خیلی کند بارگذاری می شود. اگر کمتر تسلیم شویم، زمان بیشتری طول می کشد تا مرورگر به رویدادهای کاربر پاسخ دهد و مردم ناامید می شوند. سرگرم کننده نیست.

نموداری که نشان می دهد وقتی کارهای طولانی JS را اجرا می کنید، مرورگر زمان کمتری برای ارسال رویدادها دارد.

در فیس‌بوک، می‌خواستیم ببینیم که اگر رویکرد جدیدی برای بارگذاری ارائه کنیم که این مبادله ناامیدکننده را از بین ببرد، چه شکلی می‌شود. ما در این مورد با دوستان خود در Chrome تماس گرفتیم و پیشنهاد isInputPending() را ارائه کردیم. isInputPending() اولین API است که از مفهوم وقفه برای ورودی های کاربر در وب استفاده می کند و به جاوا اسکریپت اجازه می دهد بدون تسلیم شدن به مرورگر، ورودی را بررسی کند.

نموداری که نشان می‌دهد isInputPending() به JS شما اجازه می‌دهد تا بررسی کند که آیا ورودی کاربر در حال تعلیق وجود دارد، بدون اینکه به طور کامل اجرا را به مرورگر برگرداند.

از آنجایی که علاقه به API وجود داشت، ما با همکاران خود در Chrome برای پیاده سازی و ارسال این ویژگی در Chromium شریک شدیم. با کمک مهندسان کروم، وصله‌ها را پشت یک آزمایش اولیه قرار دادیم (که راهی برای Chrome برای آزمایش تغییرات و دریافت بازخورد از توسعه‌دهندگان قبل از انتشار کامل API است).

ما اکنون بازخوردهایی را از آزمایش اولیه و سایر اعضای کارگروه عملکرد وب W3C گرفته‌ایم و تغییراتی را در API اعمال کرده‌ایم.

مثال: زمان‌بندی بازدهی

فرض کنید برای بارگذاری صفحه خود یکسری کار برای مسدود کردن نمایشگر دارید، به عنوان مثال ایجاد نشانه گذاری از مؤلفه ها، فاکتور برداری از اعداد اول، یا فقط کشیدن یک چرخنده بارگیری جالب. هر یک از اینها به یک آیتم کاری مجزا تقسیم می شود. با استفاده از الگوی زمانبندی، بیایید طرح کنیم که چگونه می توانیم کار خود را در تابع فرضی processWorkQueue() پردازش کنیم:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

با فراخوانی processWorkQueue() بعداً در یک ماکروتسک جدید از طریق setTimeout() ، به مرورگر این توانایی را می‌دهیم که تا حدودی به ورودی پاسخگو باشد (می‌تواند کنترل‌کننده‌های رویداد را قبل از ازسرگیری کار اجرا کند) در حالی که همچنان می‌تواند نسبتاً بدون وقفه اجرا شود. اگرچه، ممکن است برای مدت طولانی توسط کارهای دیگری که می‌خواهند حلقه رویداد را کنترل کنند، برنامه‌ریزی کنیم، یا به یک QUANTUM میلی‌ثانیه تأخیر رویداد اضافه برسیم.

این اشکالی ندارد، اما آیا می توانیم بهتر عمل کنیم؟ قطعا!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

با معرفی یک فراخوانی به navigator.scheduling.isInputPending() ، می‌توانیم به ورودی سریع‌تر پاسخ دهیم و در عین حال مطمئن شویم که کار مسدود کردن نمایشگر ما بدون وقفه اجرا می‌شود. اگر ما علاقه ای به کار با چیز دیگری جز ورودی (مثلاً نقاشی) تا پایان کار نداریم، می توانیم به راحتی طول QUANTUM را نیز افزایش دهیم.

به طور پیش فرض، رویدادهای "پیوسته" از isInputPending() برگردانده نمی شوند. اینها شامل mousemove ، pointermove ، و موارد دیگر است. اگر شما نیز علاقه مند به تسلیم شدن برای این موارد هستید، مشکلی نیست. با ارائه یک شی به isInputPending() با تنظیم includeContinuous روی true ، خوب می‌رویم:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

همین! چارچوب‌هایی مانند React با استفاده از منطق مشابه، پشتیبانی isInputPending() را در کتابخانه‌های زمان‌بندی هسته خود ایجاد می‌کنند. امیدواریم این امر باعث شود توسعه‌دهندگانی که از این چارچوب‌ها استفاده می‌کنند، بتوانند از پشت صحنه isInputPending() بدون بازنویسی قابل توجه بهره ببرند.

تسلیم شدن همیشه بد نیست

شایان ذکر است که بازده کمتر راه حل مناسبی برای هر موردی نیست. دلایل زیادی برای بازگرداندن کنترل به مرورگر وجود دارد به غیر از پردازش رویدادهای ورودی، مانند اجرای رندر و اجرای اسکریپت های دیگر در صفحه.

مواردی وجود دارد که مرورگر نمی تواند رویدادهای ورودی معلق را به درستی نسبت دهد. به طور خاص، تنظیم کلیپ‌ها و ماسک‌های پیچیده برای iframe‌های متقاطع ممکن است منفی‌های کاذب را گزارش کند (به عنوان مثال isInputPending() ممکن است هنگام هدف‌گیری این فریم‌ها به طور غیرمنتظره‌ای false را بازگرداند. مطمئن شوید که اگر سایت شما نیاز به تعامل با زیرفریم های سبک دار دارد، اغلب به اندازه کافی نتیجه می دهید.

حواستان به صفحات دیگری نیز باشد که حلقه رویداد را به اشتراک می گذارند. در پلتفرم‌هایی مانند Chrome for Android، اشتراک‌گذاری یک حلقه رویداد برای چندین منبع بسیار رایج است. isInputPending() اگر ورودی به یک فریم متقاطع ارسال شود، هرگز true برنمی‌گرداند، و بنابراین صفحات پس‌زمینه ممکن است در واکنش‌پذیری صفحات پیش‌زمینه اختلال ایجاد کنند. ممکن است بخواهید هنگام انجام کار در پس‌زمینه با استفاده از صفحه نمایش API، کاهش، به تعویق انداختن یا تسلیم شدن را بیشتر کنید.

ما شما را تشویق می کنیم که با احتیاط isInputPending() استفاده کنید. اگر کاری برای مسدود کردن کاربر وجود ندارد، با تسلیم بیشتر در حلقه رویداد با دیگران مهربان باشید. کارهای طولانی می تواند مضر باشد .

بازخورد

  • بازخورد خود را در مورد مشخصات در مخزن is-input-pending بگذارید.
  • با @acomminos (یکی از نویسندگان مشخصات) در توییتر تماس بگیرید.

نتیجه گیری

ما از اینکه isInputPending() در حال راه اندازی است و توسعه دهندگان می توانند از امروز شروع به استفاده از آن کنند، هیجان زده هستیم. این API اولین باری است که فیس بوک یک وب API جدید ایجاد می کند و آن را از انکوباسیون ایده به پیشنهاد استاندارد تا ارسال واقعی در مرورگر تبدیل می کند. مایلیم از همه کسانی که ما را در رسیدن به این نقطه یاری کردند تشکر کنیم و به همه افرادی که در Chrome به ما کمک کردند تا این ایده را بسط دهیم و آن را ارسال کنیم، یک فریاد ویژه ارائه دهیم!

عکس قهرمان توسط Will H McMahan در Unsplash .