Một vòng lặp của event loop trong trình duyệt đại khái: chạy một task → rút cạn microtask → (nếu tới lúc vẽ) chạy callback rAF → style/layout/paint → sang vòng sau.
js
setTimeout(() => console.log('timeout'), 0)
requestAnimationFrame(() => console.log('raf'))
queueMicrotask(() => console.log('micro'))
console.log('sync')
// sync, micro, rồi timeout / raf tuỳ thời điểm khung hìnhNhững điểm cần nắm:
- Microtask được rút cạn sau mỗi task, và cả sau mỗi callback rAF — nên một microtask sinh microtask liên tục sẽ chặn render vĩnh viễn (trang treo dù không có vòng
whilenào). - rAF chạy ngay trước bước vẽ, tối đa theo tần số màn hình (~60fps) và bị tạm dừng khi tab ẩn — đó là lý do dùng nó cho animation thay vì
setTimeout(16), vốn trôi dần và vẫn chạy ở tab nền. setTimeoutbị kẹp tối thiểu 4ms khi lồng quá 5 cấp; nó không đảm bảo chạy trước hay sau rAF — phụ thuộc lúc đó có phải thời điểm vẽ hay không.- Đo layout (
offsetHeight,getBoundingClientRect) ngay sau khi ghi style gây forced reflow; gom đọc rồi ghi trong cùng callback rAF.
Cần chạy sau khi khung hình đã vẽ (đo hiệu năng, đo kích thước thật) thì lồng requestAnimationFrame bên trong requestAnimationFrame, hoặc dùng setTimeout(..., 0) bên trong rAF.