فشار شدید تایمرهای JS زنجیر شده در Chrome 88 آغاز شد

کروم 88 (ژانویه 2021) تایمرهای جاوا اسکریپت زنجیر شده را برای صفحات مخفی در شرایط خاص به شدت کنترل می کند. این کار باعث کاهش مصرف CPU می شود که مصرف باتری را نیز کاهش می دهد. برخی از موارد لبه ای وجود دارد که این رفتار را تغییر می دهد، اما تایمرها اغلب در مواردی استفاده می شوند که یک API متفاوت کارآمدتر و قابل اعتمادتر باشد.

خوب، این اصطلاح بسیار سنگین و کمی مبهم بود. بیایید در…

اصطلاحات

صفحات مخفی

به طور کلی، hidden به این معنی است که یک برگه دیگر فعال است، یا پنجره کوچک شده است، اما مرورگرها ممکن است یک صفحه را پنهان در نظر بگیرند که محتوای آن کاملاً قابل مشاهده نباشد. برخی از مرورگرها در اینجا فراتر از سایرین هستند، اما همیشه می‌توانید از API مشاهده صفحه برای ردیابی زمانی که مرورگر فکر می‌کند قابلیت مشاهده تغییر کرده است استفاده کنید.

تایمرهای جاوا اسکریپت

منظور من از تایمرهای جاوا اسکریپت setTimeout و setInterval است که به شما امکان می دهد در آینده یک تماس برگشتی را برنامه ریزی کنید. تایمرها مفید هستند و از بین نمی‌روند، اما گاهی اوقات از آنها برای بیان اینکه چه زمانی یک رویداد کارآمدتر و دقیق‌تر است، استفاده می‌شود.

تایمر زنجیر شده

اگر setTimeout در همان کار با callback setTimeout فراخوانی کنید، فراخوانی دوم «زنجیره شده» است. با setInterval ، هر تکرار بخشی از زنجیره است. این ممکن است با کد ساده تر قابل درک باشد:

let chainCount = 0;

setInterval(() => {
  chainCount++;
  console.log(`This is number ${chainCount} in the chain`);
}, 500);

و:

let chainCount = 0;

function setTimeoutChain() {
  setTimeout(() => {
    chainCount++;
    console.log(`This is number ${chainCount} in the chain`);
    setTimeoutChain();
  }, 500);
}

گاز گاز چگونه کار می کند

گاز در مراحل زیر انجام می شود:

حداقل دریچه گاز

این برای تایمرهایی اتفاق می افتد که زمانی برنامه ریزی می شوند که هر یک از موارد زیر درست باشد:

  • صفحه قابل مشاهده است.
  • صفحه در 30 ثانیه گذشته صداهایی ایجاد کرده است. این می‌تواند از هر یک از APIهای تولید کننده صدا باشد، اما آهنگ صوتی بی‌صدا به حساب نمی‌آید.

تایمر خاموش نمی شود، مگر اینکه مهلت زمانی درخواستی کمتر از 4 میلی ثانیه باشد، و تعداد زنجیره ها 5 یا بیشتر باشد، در این صورت تایم اوت روی 4 میلی ثانیه تنظیم می شود. این جدید نیست. مرورگرها سالها این کار را انجام داده اند.

گاز دادن

این برای تایمرهایی اتفاق می‌افتد که زمانی برنامه‌ریزی می‌شوند که حداقل گاز اعمال نشود و هر یک از موارد زیر صادق است:

  • تعداد زنجیر کمتر از 5 است.
  • این صفحه برای کمتر از 5 دقیقه مخفی شده است.
  • WebRTC در حال استفاده است. به طور خاص، یک RTCPeerConnection با یک RTCDataChannel "باز" ​​یا یک MediaStreamTrack "زنده" وجود دارد.

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

دریچه گاز شدید

بسیار خوب، این بیت جدید در Chrome 88 است. درگیری شدید برای تایمرهایی اتفاق می‌افتد که زمانی برنامه‌ریزی می‌شوند که هیچ یک از حداقل‌ترین شرایط throttling یا throttling اعمال نشود، و همه شرایط زیر صادق است:

  • صفحه برای بیش از 5 دقیقه مخفی شده است.
  • تعداد زنجیر 5 یا بیشتر است.
  • صفحه حداقل 30 ثانیه ساکت بوده است.
  • WebRTC استفاده نمی شود.

در این صورت، مرورگر تایمرهای این گروه را یک بار در دقیقه بررسی می کند. مانند قبل، این بدان معنی است که تایمرها در این بررسی های دقیقه به دقیقه با هم جمع می شوند.

راه حل ها

معمولاً جایگزین بهتری برای تایمر وجود دارد، یا می‌توان تایمرها را با چیز دیگری ترکیب کرد تا نسبت به CPU و عمر باتری مهربان‌تر باشد.

نظرسنجی ایالتی

این رایج ترین (سو) استفاده از تایمرها است، جایی که از آنها برای بررسی مداوم یا نظرسنجی برای دیدن اینکه آیا چیزی تغییر کرده است استفاده می شود. در بیشتر موارد، یک معادل فشار وجود دارد، جایی که چیزی به شما در مورد تغییر زمانی که اتفاق می‌افتد، می‌گوید، بنابراین نیازی نیست که به بررسی ادامه دهید. نگاه کنید تا ببینید آیا رویدادی وجود دارد که همان چیزی را به دست آورد.

چند نمونه:

همچنین اگر می‌خواهید اعلان را در یک زمان خاص نشان دهید، راه‌اندازهای اعلان وجود دارد.

انیمیشن

انیمیشن یک چیز بصری است، بنابراین زمانی که صفحه پنهان است نباید از زمان CPU استفاده کند.

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

اگر می‌توانید کل انیمیشن خود را از قبل اعلام کنید، از انیمیشن‌های CSS یا API انیمیشن‌های وب استفاده کنید. اینها مزایای مشابه requestAnimationFrame را دارند، اما مرورگر می‌تواند بهینه‌سازی‌های اضافی مانند ترکیب خودکار را انجام دهد و به طور کلی استفاده از آنها آسان‌تر است.

اگر انیمیشن شما فریم پایینی دارد (مانند یک مکان نما که چشمک می زند)، تایمرها هنوز بهترین گزینه در حال حاضر هستند، اما می توانید آنها را با requestAnimationFrame ترکیب کنید تا بهترین های هر دو دنیا را به دست آورید:

function animationInterval(ms, signal, callback) {
  const start = document.timeline.currentTime;

  function frame(time) {
    if (signal.aborted) return;
    callback(time);
    scheduleFrame(time);
  }

  function scheduleFrame(time) {
    const elapsed = time - start;
    const roundedElapsed = Math.round(elapsed / ms) * ms;
    const targetNext = start + roundedElapsed + ms;
    const delay = targetNext - performance.now();
    setTimeout(() => requestAnimationFrame(frame), delay);
  }

  scheduleFrame(start);
}

استفاده:

const controller = new AbortController();

// Create an animation callback every second:
animationInterval(1000, controller.signal, time => {
  console.log('tick!', time);
});

// And stop it:
controller.abort();

تست کردن

این تغییر برای همه کاربران Chrome در Chrome 88 (ژانویه 2021) فعال خواهد شد. در حال حاضر برای 50٪ از کاربران Chrome Beta، Dev و Canary فعال است. اگر می‌خواهید آن را آزمایش کنید، هنگام راه‌اندازی Chrome Beta، Dev یا Canary از این پرچم خط فرمان استفاده کنید:

--enable-features="IntensiveWakeUpThrottling:grace_period_seconds/10,OptOutZeroTimeoutTimersFromThrottling,AllowAggressiveThrottlingWithWebSocket"

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

آینده

از آنجایی که تایمرها منبع استفاده بیش از حد از CPU هستند، ما به دنبال راه‌هایی هستیم که می‌توانیم آن‌ها را بدون شکستن محتوای وب مهار کنیم، و API‌هایی که می‌توانیم آن‌ها را اضافه/تغییر دهیم تا موارد استفاده را برآورده کنیم. من شخصاً می‌خواهم نیاز به animationInterval را به نفع تماس‌های متحرک با فرکانس پایین کارآمد حذف کنم. اگر سؤالی دارید، لطفاً در توییتر با من تماس بگیرید !

عکس هدر توسط Heather Zabriskie در Unsplash .