Hai API để đặt công việc vào đúng thời điểm của vòng đời khung hình.
javascript
// rAF — chạy NGAY TRƯỚC lần vẽ tiếp theo (~16.6ms/khung ở 60Hz)
function animate(timestamp) {
el.style.transform = `translateX(${(timestamp / 10) % 300}px)`
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
// rIC — chạy khi browser RẢNH giữa các khung hình
requestIdleCallback((deadline) => {
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && queue.length) {
process(queue.shift()) // dừng ngay khi hết thời gian rảnh
}
}, { timeout: 2000 }) // timeout: quá 2s chưa rảnh thì vẫn gọi — lúc đó timeRemaining() = 0, nên phải xét thêm didTimeoutrequestAnimationFrame | requestIdleCallback | |
|---|---|---|
| Chạy khi | trước mỗi lần repaint | browser đang rảnh |
| Dùng cho | animation, đọc/ghi layout | analytics, prefetch, việc không gấp |
| Tab ở nền | tạm dừng | vẫn chạy thưa |
Vì sao rAF hơn setInterval(fn, 16) cho animation: nó đồng bộ với chu kỳ vẽ thật của màn hình (kể cả màn 120Hz), và tự dừng khi tab ẩn — không đốt pin vô ích.
Hỗ trợ: requestIdleCallback có trên Chrome/Firefox từ lâu và Safari 18+ (2024).
Cách hiện đại hơn để cắt long task:
javascript
for (const item of hugeList) {
process(item)
if (needsYield()) await scheduler.yield() // Chrome/Edge 129+, Firefox 142+ — Safari chưa có
}scheduler.yield() nhường lượt cho browser render rồi quay lại đúng chỗ với độ ưu tiên cao, tránh được nhược điểm của setTimeout(0) là bị xếp sau mọi task khác.