setInterval vs requestAnimationFrame
定时执行的任务与requestAnimationFrame api比较
功能
setInterval中为宏任务,在执行到setInterval时会推入的宏任务队列,在微任务执行完毕之后才回去执行宏任务。
由于EventLoop机制,导致setInterval的执行事件间隔并不是准确的,会受微任务队列执行的时长、CPU load等影响。
执行第一次宏任务setInterval回调之后,主线程开始执行微任务,如果微任务没有执行完毕,那么下次宏任务的执行时机会延迟,并不会按照传入的时长去执行
requestAnimationFrame 是宏任务还是微任务?
在宏任务前新增一个微任务,后新增一个requestAnimationFrame
- 如果
requestAnimationFrame是微任务
则输出:微任务 -> requestAnimationFrame -> 宏任务
- 如果
requestAnimationFrame是宏任务
则输出:微任务 -> 宏任务 -> requestAnimationFrame
js
const promise = new Promise((resolve) => {
console.log('promise');
resolve();
});
promise.then((res) => {
console.log('promise then');
});
setTimeout(() => {
console.log('setTimeout');
});
requestAnimationFrame(() => {
console.log('requestAnimationFrame');
});
/*
如果是 微任务,输出顺序微:
promise
promise then
requestAnimationFrame
setTimeout
*/
/*
如果是 宏任务,输出顺序微:
promise
promise then
setTimeout
requestAnimationFrame
*/可知requestAnimationFrame是宏任务,那么既然是宏任务,为什么不会出现时长延迟的问题?
简单来说,即使两者都是宏任务,但是执行时机不同,setTimeout执行时机早于requestAnimationFrame

在页面重新绘制之前有渲染时机的判断Rendering opportunities,用来减少不必要的渲染。也就是说,如果setTimeout 回调执行了多次之后,浏览器发现还不到渲染时机,那么便不会执行重绘。例如在1帧之内执行多次setTimeout,并不会立刻渲染,这会导致渲染的DOM出现掉帧
参考:
【1】requestAnimationFrame 执行机制探索
【2】8.6 Timers

