requestAnimationFrame和requestIdleCallback是宏任务还是微任务

大家好,我是雷布斯。

昨天团队的 uncle13老师在群里分享了这么一个面试题“requestAnimationFrame与宏任务区别”,是一位学员在面试某大厂的时候被问到的。

今天就分享一篇文章,相信能帮助大家解答这个问题。

一、思考

大家都知道异步任务分为宏任务和微任务,要搞清楚 requestAnimationFramerequestIdleCallback 是宏任务还是微任务就必须要搞清楚下面几个问题:

  1. 浏览器在每一轮Event Loop事件循环中都会去渲染屏幕吗?
  2. requestAnimationFrame在哪个阶段执行,是在渲染前还是渲染后?是在微任务执行前还是执行后?
  3. requestIdleCallback在哪个阶段执行,是在渲染前还是渲染后?是在微任务执行前还是执行后?

二、任务的执行时机

在浏览器的Event Loop中是有多个任务队列的,每个任务队列的执行时机是不一样的,下面直接上干货,说说浏览器执行任务的顺序

  1. 从task任务队列中取第一个task(比如setTimeout、setIntervel的回调,也可以将同一轮循环中的所有同步代码看作是一个宏任务),执行它。
  2. 执行微任务队列里的所有微任务。
  3. 浏览器判断是否更新渲染屏幕,如果需要重新绘制,则执行步骤4-13,如果不需要重新绘制,则流程回到步骤1,这样不断循环。
  4. 触发resize、scroll事件,建立媒体查询。
  5. 建立css动画。
  6. 执行requestAnimationFrame回调。
  7. 执行 IntersectionObserver 回调。
  8. 更新渲染屏幕。
  9. 浏览器判断当前帧是否还有空闲时间,如果有空闲时间,则执行步骤10-12。
  10. 从 requestIdleCallback回调函数队列中取第一个,执行它。
  11. 执行微任务队列里的所有微任务。
  12. 流程回到步骤9,直到requestIdleCallback回调函数队列清空或当前帧没有空闲时间。
  13. 流程回到步骤1,这样不断循环。

三、代码验证

我们可以写一些代码到Chrome浏览器中验证一下,看看 requestAnimationFramerequestIdleCallback 的执行顺序是怎样的。

运行以下这段代码

requestAnimationFrame(()=>{
    console.log(111);
    setTimeout(() => {
        console.log(222);
    });
    Promise.resolve().then(() => {
        console.log(333);
    });
})

requestAnimationFrame(() => {
    console.log(444);
    Promise.resolve().then(() => {
        console.log(555);
    });
})

输出结果

运行以下这段代码

requestIdleCallback(() => {
    console.log(111);
    setTimeout(() => {
        console.log(222);
    })
    Promise.resolve().then(() => {
        console.log(333);
    })
})

requestIdleCallback(() => {
    console.log(444);
    Promise.resolve().then(() => {
        console.log(555);
    })
})

输出结果

运行以下这段代码

Promise.resolve().then(() => {
    console.log(111);
    setTimeout(() => {
        console.log(222);
    })
    Promise.resolve().then(() => {
        console.log(333);
    })
})

Promise.resolve().then(() => {
    console.log(444);
    Promise.resolve().then(() => {
        console.log(555);
    })
})

输出结果

四、总结

  1. requestAnimationFrame和requestIdleCallback是和宏任务性质一样的任务,只是他们的执行时机不同而已。也有人说它们既不是宏任务也不是微任务,其实我们不必纠结这个,我们所要做的就是知道他们的执行时机就好。
  2. 浏览器在每一轮Event Loop事件循环中不一定会去重新渲染屏幕,会根据浏览器刷新率以及页面性能或是否后台运行等因素判断的,浏览器的每一帧是比较固定的,会尽量保持60Hz的刷新率运行,每一帧中间可能会进行多轮事件循环。
  3. requestAnimationFrame回调的执行与task和microtask无关,而是与浏览器是否渲染相关联的。它是在浏览器渲染前,在微任务执行后执行。
  4. requestIdleCallback是在浏览器渲染后有空闲时间时执行,如果requestIdleCallback设置了第二个参数timeout,则会在超时后的下一帧强制执行。

最后

还没有使用过我们刷题网站(https://fe.ecool.fun/)或者刷题小程序的同学,如果最近最近准备或者正在找工作,千万不要错过,我们的题库主打无广告和更新快哦~。

老规矩,也给我们团队的辅导服务打个广告。

原文作者:River_何

原文链接:https://juejin.cn/post/7134972903816167455