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 bộ hẹn giờ JavaScript theo chuỗi 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 giảm mức sử dụng pin. Có một số trường hợp đặc biệt mà việc này sẽ thay đổi hành vi, nhưng bộ hẹn giờ thường được sử dụng khi một API khác sẽ hiệu quả và đáng tin cậy hơn.

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

Thuật ngữ

Trang bị ẩn

Thông thường, trạng thái ẩn có nghĩa là một thẻ khác đang hoạt động hoặc cửa sổ đã được thu nhỏ, nhưng trình duyệt có thể coi một trang là ẩ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 đi xa hơn các trình duyệt khác ở đây, nhưng bạn luôn có thể sử dụng API chế độ hiển thị của trang để theo dõi thời điểm trình duyệt cho rằng chế độ hiển thị đã thay đổi.

Bộ hẹn giờ JavaScript

Trình hẹn giờ JavaScript mà tôi đề cập đến là setTimeoutsetInterval, cho phép bạn lên lịch gọi lại vào lúc nào đó trong tương lai. Bộ hẹn 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 sẽ hiệu quả và chính xác hơn.

Bộ hẹn giờ theo chuỗi

Nếu bạn gọi setTimeout trong cùng một tác vụ với lệnh gọi lại setTimeout, thì lệnh gọi thứ hai sẽ được "chuỗi". Với setInterval, mỗi lần lặp là một phần của chuỗi. Bạn có thể dễ hiểu hơn bằ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 tính năng điều tiết

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

Điều tiết ở mức tối thiểu

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

  • Trang hiển thị.
  • Trang đã phát ra âm thanh trong 30 giây qua. Âm thanh này có thể đến từ bất kỳ API tạo âm thanh nào, nhưng không tính đến bản âm thanh không có âm thanh.

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

Điều tiết

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

  • Số lượng chuỗi nhỏ hơn 5.
  • Trang đã bị ẩn chưa đến 5 phút.
  • WebRTC đang được sử dụng. Cụ thể, có một RTCPeerConnection với RTCDataChannel "mở" hoặc MediaStreamTrack "trực tiếp".

Trình duyệt sẽ kiểm tra bộ 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 bộ hẹn giờ có thời gian chờ tương tự sẽ được gộp lại với nhau, hợp nhất thời gian mà thẻ cần để chạy mã. Điều này cũng không mới; các trình duyệt đã thực hiện việc này ở một mức độ nào đó trong nhiều năm.

Điều tiết chuyên sâu

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

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

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

Giải pháp

Thường thì có một giải pháp thay thế tốt hơn cho bộ hẹn giờ hoặc bạn có thể kết hợp bộ hẹn giờ với một giải pháp khác để giúp CPU và thời lượng pin hoạt động hiệu quả hơn.

Thăm dò trạng thái

Đây là cách sử dụng (sai) đồng hồ hẹn giờ phổ biến nhất, trong đó đồng hồ hẹn giờ đượ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 các trường hợp, sẽ có một lệnh đẩy tương đương, trong đó hệ thống sẽ thông báo cho bạn về thay đổi khi thay đổi xảy ra, vì vậy, bạn không cần phải liên tục kiểm tra. Hãy xem liệu có sự kiện nào đạt được cùng một mục tiêu hay không.

Một số ví dụ:

Ngoài ra còn có trình kích hoạt thông báo 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 nội dung trực quan, vì vậy, ảnh động không được sử dụng thời gian CPU khi trang bị ẩn.

requestAnimationFrame tốt hơn nhiều trong việc lên lịch công việc ảnh động so với bộ hẹn giờ JavaScript. Phương thức 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 có được khoảng thời gian tối đa để tạo khung hình đó. Ngoài ra, requestAnimationFrame sẽ đợi trang hiển thị, vì vậy, requestAnimationFrame sẽ không sử dụng CPU khi trang bị ẩn.

Nếu có thể khai báo toàn bộ ảnh động trước, hãy cân nhắc sử dụng ảnh động CSS hoặc API ảnh động trên web. Các phương thức này có cá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 và thường dễ sử dụng hơn.

Nếu ảnh động của bạn có tốc độ khung hình thấp (chẳng hạn như con trỏ nhấp nháy), thì bộ hẹn giờ vẫn là lựa chọn tốt nhất hiện tại, nhưng bạn có thể kết hợp bộ hẹn giờ với requestAnimationFrame để tận dụng cả hai phương pháp:

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

Thử nghiệm

Thay đổi này sẽ được bật 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, Chrome Dev và Chrome Canary. Nếu bạn muốn kiểm thử tính năng này, 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 sẽ kích hoạt tính năng điều tiết dữ liệu mạnh mẽ sau 10 giây kể từ khi trang bị ẩn, thay vì 5 phút đầy đủ, giúp bạn dễ dàng thấy được tác động của tính năng điều tiết dữ liệu.

Tương lai

Vì bộ hẹn giờ là nguồn sử dụng CPU quá mức, nên chúng ta sẽ tiếp tục xem xét các cách để điều tiết bộ hẹn giờ mà không làm hỏng nội dung web, cũng như các API mà chúng ta 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 để chuyển sang sử dụng lệnh gọi lại ảnh động tần số thấp hiệu quả. Nếu bạn có thắc mắc, vui lòng liên hệ với tôi trên Twitter!

Ảnh đầu trang do Heather Zabriskie chụp trên Unsplash.