Chế độ điều tiết mạnh các đồng hồ hẹn giờ JS bị chuỗi bắt đầu trong Chrome 88

Jake Archibald
Jake Archibald

Chrome 88 (tháng 1 năm 2021) sẽ điều tiết mạnh các đồng hồ hẹn giờ JavaScript chuỗi được điều chỉnh cho các trang ẩn trong một số điều kiện cụ thể. Việc này sẽ làm giảm mức sử dụng CPU, đồng thời làm giảm mức sử dụng pin. Có một số trường hợp đặc biệt mà điều này sẽ thay đổi hành vi, nhưng bộ tính giờ thường được sử dụng trong trường hợp API khác sẽ hiệu quả và đáng tin cậy hơn.

Được rồi, đó là những thuật ngữ khá nặng nề và có hơi mơ hồ. Hãy cùng tìm hiểu sâu hơn...

Thuật ngữ

Trang bị ẩn

Nói chung, bị ẩn có nghĩa là một thẻ khác đang hoạt động hoặc cửa sổ đã được thu nhỏ. Tuy nhiên, các trình duyệt có thể xem một trang bị ẩn bất cứ khi nào nội dung của trang đó hoàn toàn không hiển thị. Một số trình duyệt có tính năng cao hơn những trình duyệt khác ở đây, nhưng bạn luôn có thể sử dụng API chế độ hiển thị trang để theo dõi thời điểm trình duyệt cho rằng chế độ hiển thị đã thay đổi.

Bộ tính giờ JavaScript

Hẹn giờ JavaScript, tôi muốn nói đến setTimeoutsetInterval, cho phép bạn lên lịch gọi lại vào một thời điểm nào đó trong tương lai. Bộ tính giờ rất hữu ích và sẽ không biến mất, nhưng đôi khi chúng được dùng để thăm dò trạng thái khi một sự kiện có thể hiệu quả hơn và chính xác hơn.

Đồng hồ hẹn giờ theo chuỗi

Nếu bạn gọi setTimeout trong cùng một tác vụ như lệnh gọi lại setTimeout, thì lệnh gọi thứ hai sẽ được tạo "chuỗi". Với setInterval, mỗi vòng lặp đều là một phần của chuỗi. Điều này có thể dễ hiểu hơn khi dùng mã:

let chainCount = 0;

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

Và:

let chainCount = 0;

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

Cách hoạt động của chế độ điều tiết

Quá trình điều tiết diễn ra theo các giai đoạn:

Chế độ điều tiết tối thiểu

Điều này sẽ xảy ra với các đồng hồ hẹn giờ được lên lịch khi bất kỳ trường hợp nào sau đây là đúng:

  • Trang hiển thị.
  • Trang đã tạo ra tiếng ồn trong 30 giây qua. Tiếng này có thể từ bất kỳ API tạo âm thanh nào, nhưng bản âm thanh im lặng thì không được tính.

Bộ tính giờ không bị điều tiết, trừ phi thời gian chờ được yêu cầu dưới 4 mili giây và số lượng chuỗi từ 5 trở lên, trong trường hợp đó, thời gian chờ được đặt thành 4 mili giây. Đây không phải là điều mới mẻ; các trình duyệt đã làm điều này trong nhiều năm qua.

Điều tiết

Điều này xảy ra với các đồng hồ hẹn giờ được lên lịch khi không áp dụng chế độ điều tiết tối thiểubất kỳ điều nào sau đây là đúng:

  • Số chuỗi nhỏ hơn 5.
  • Trang này đã được ẩn trong chưa đầy 5 phút.
  • WebRTC đang được sử dụng. Cụ thể, có một RTCPeerConnection với RTCDataChannel "mở" hoặc MediaStreamTrack "đang hoạt động".

Trình duyệt sẽ kiểm tra đồng hồ hẹn giờ trong nhóm này một lần mỗi giây. Vì chỉ được kiểm tra một lần mỗi giây, nên các đồng hồ hẹn giờ có thời gian chờ tương tự sẽ phối hợp cùng nhau, từ đó hợp nhất thời gian thẻ cần chạy mã. Đây cũng không phải là điều mới; các trình duyệt đã làm điều này ở một mức độ nào đó trong nhiều năm qua.

Điều tiết tăng cường

Được rồi, sau đây là bit mới trong Chrome 88. Chế độ điều tiết tăng cường sẽ xảy ra với các đồng hồ hẹn giờ được lên lịch khi không có điều kiện điều tiết tối thiểu hoặc điều tiết nào được áp dụng và tất cả các điều kiện sau đây đều đúng:

  • Trang này đã bị ẩn trong hơn 5 phút.
  • Số lượng chuỗi từ 5 trở lên.
  • Trang đã tắt tiếng ít nhất 30 giây.
  • WebRTC không được dùng.

Trong trường hợp này, trình duyệt sẽ kiểm tra đồng hồ hẹn giờ trong nhóm này mỗi phút một lần. Tương tự như trước đây, điều này có nghĩa là các đồng hồ hẹn giờ sẽ được gộp theo lô trong các bước kiểm tra từng phút một.

Giải pháp

Thường có một giải pháp thay thế tốt hơn cho bộ tính giờ hoặc bạn có thể kết hợp đồng hồ hẹn giờ với một tính năng khác phù hợp hơn với CPU và thời lượng pin.

Cuộc thăm dò ý kiến trạng thái

Đây là trường hợp phổ biến nhất (nhầm) sử dụng đồng hồ hẹn giờ, trong đó chúng được dùng để liên tục kiểm tra hoặc thăm dò ý kiến để xem có gì thay đổi hay không. Trong hầu hết trường hợp, sẽ có một push tương đương, trong đó thông tin cho bạn biết về sự thay đổi khi nó xảy ra, vì vậy, bạn không cần phải tiếp tục kiểm tra. Quan sát xem có sự kiện nào đạt được mục tiêu tương tự không.

Một số ví dụ:

Ngoài ra, tính năng kích hoạt thông báo cũng sẽ được kích hoạt nếu bạn muốn hiển thị thông báo vào một thời điểm cụ thể.

Hoạt ảnh

Ảnh động là một yếu tố trực quan, vì vậy ảnh động không nên sử dụng thời gian của CPU khi trang bị ẩn.

requestAnimationFrame khả năng lên lịch ảnh động hiệu quả hơn nhiều so với bộ tính giờ JavaScript. Khung này đồng bộ hoá với tốc độ làm mới của thiết bị, đảm bảo bạn chỉ nhận được một lệnh gọi lại cho mỗi khung hình có thể hiển thị và bạn sẽ nhận được khoảng thời gian tối đa để tạo khung hình đó. Ngoài ra, requestAnimationFrame sẽ chờ trang hiển thị, vì vậy, nó không sử dụng bất kỳ CPU nào khi trang bị ẩn.

Nếu bạn có thể khai báo trước toàn bộ ảnh động, hãy cân nhắc sử dụng Ảnh động CSS hoặc API ảnh động trên web. Các lớp này có ưu điểm giống như requestAnimationFrame, nhưng trình duyệt có thể thực hiện thêm các hoạt động tối ưu hoá như kết hợp tự động, đồng thời các trình duyệt này thường dễ sử dụng hơn.

Nếu ảnh động của bạn có tốc độ khung hình thấp (như con trỏ nhấp nháy), thì đồng hồ hẹn giờ vẫn là lựa chọn tốt nhất ngay bây giờ. Tuy nhiên, bạn có thể kết hợp đồng hồ hẹn giờ với requestAnimationFrame để có được hiệu ứng tốt nhất ở cả hai mặt:

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);
}

Cách sử dụng:

const controller = new AbortController();

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

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

Kiểm thử

Thay đổi này sẽ được áp dụng cho tất cả người dùng Chrome trong Chrome 88 (tháng 1 năm 2021). Tính năng này hiện được bật cho 50% người dùng Chrome Beta, Nhà phát triển và Canary. Nếu bạn muốn kiểm thử, hãy sử dụng cờ dòng lệnh này khi chạy Chrome Beta, Dev hoặc Canary:

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

Đối số grace_period_seconds/10 khiến chế độ điều tiết ở cường độ cao hoạt động sau 10 giây trang bị ẩn, thay vì đủ 5 phút, giúp bạn dễ dàng thấy được tác động của chế độ điều tiết.

Tương lai

Vì bộ tính giờ là một nguồn sử dụng CPU quá mức, nên chúng tôi sẽ tiếp tục tìm cách điều tiết chúng mà không làm hỏng nội dung trên web, đồng thời các API có thể thêm/thay đổi để đáp ứng các trường hợp sử dụng. Cá nhân tôi muốn loại bỏ nhu cầu sử dụng animationInterval mà thay vào đó là gọi lại ảnh động hiệu quả với tần suất thấp. Nếu bạn có thắc mắc, vui lòng liên hệ với tôi trên Twitter!

Ảnh tiêu đề của Heather Zabriskie trên Unsplash.