rAF在EventLoop的表现

最近会被问到EventLoop的相关问题,这个只要对任务微任务理解到位一般没啥问题,但有次被问到“requestAnimation的执行时机是什么”,答约:“rAF是在浏览器重新渲染屏幕之前执行”,之后被追问:“那它属于宏任务还是微任务,他在时间循环的执行时机是什么样的”。

当时听到这个问题一时有点懵,因为按照之前的经验和理解,rAF一般做动画场景很少遇到宏任务微任务的问题,我感觉它跟宏任务微任务是没啥关系的,因为我看过MDN HTML5的规范关于‘Task’和‘Microtask’的划分,是没看到它的身影的。下来后研究了下,比如下面这段代码,大家可以猜猜输出什么:

setTimeout(() => {
    console.log('setTimeout');
}, 0);
Promise.resolve()
    .then(() => {
        console.log(2);
    })
    .then(() => {
        console.log(3);
    });
new Promise((resolve) => {
    console.log(4);
    resolve();
})
    .then(() => {
        console.log(5);
        return 6;
    })
    .then(Promise.resolve(7))
    .then((res) => {
        console.log(res);
    });
// setTimeout(() => {
//     console.log('setTimeout2');
// });
requestAnimationFrame(() => {
    console.log('animation’');
});

你输入到浏览器,看到输出可能是:

4 2 5 3 6  animation setTimeout

这时候你是不是就把rAF归为微任务了?那你再执行的同时,滚动下浏览器,会发现输出变不一样了,变成了:

4 2 5 3 6  setTimeout animation

此时把注释打开,再加一个宏任务,执行同时滚动浏览器,发现输出

4 2 5 3 6  setTimeout setTimeout2 animation

这时,你就会发现rAF的表现又有点像last task的表现。

对这种现象,我归结表现为:

  • 如果不滚动屏幕,不需要重新绘制帧,requestAnimationFrame会在下一个宏任务之前伴随着本轮微任务执行(顺序也会按照微任务队列的顺序),,即输出'setTimeout','animation'

如果发生了滚动事件,需要重新绘制屏幕,animation最后执行.就算再加一个setTimeout宏任务,还是同理的表现
如果发生了滚动或者重绘操作,定时器和raF的执行会变得不固定
取决于当前的loop是不是render loop,可以简单理解,当前的loop是不是到了16ms且是否有raf回调。

查了不少资料,国内的很多要么没解释清楚要么瞎解释,外网stackOverFlow上有一些讨论,国内的推荐看这篇 链接,感觉还是要朝着源码和规范去理解,但其实最后浏览器差异和版本的差异表现还有不同(比如rIC的执行在chrome不同版本的执行。。)。

posted @ 2024-02-23 01:40  Lawliet__zmz  阅读(91)  评论(0编辑  收藏  举报