requestAnimationFrame在主线程任务管理中属于microtask还是macrotask?如果不是,我们如何对这种渲染端任务进行分类

问题描述 投票:0回答:1

如何应对日程影响?我做了一些测试,似乎 hooks 是在 requestAnimationFrame 之后、setTimeout 之前调用的。所以我就想知道,scheduler的真正实现是怎样的?我检查了react源代码,它似乎是基于MessageChannel api构建的。 另外,事件循环如何运行宏任务序列,例如 setTimeout/script 等?

const addMessageChannel = (performWorkUntilDeadline: any) => {
    const channel = new MessageChannel();
    const port = channel.port2;
    channel.port1.onmessage = performWorkUntilDeadline;
    port.postMessage(null);
}
const Component1 = () => {
    const [value,] = useState('---NOT INITIALISED')
    requestIdleCallback(() => {
        console.log('requestIdleCallback---')
    })
    useEffect(() => {
        console.log('useEffect---')
    }, [])
    Promise.resolve().then(() => {
        console.log('promise---')
    })
    setTimeout(() => {
        console.log('setTimeout---')
    });
    addMessageChannel(()=> {
        console.log('addMessageChannel---')
    })
    requestAnimationFrame(() => {
        console.log('requestAnimationFrame---')
    })
    return <div>{value}</div>;
}
export default Component1

浏览器控制台结果:

promise---
requestAnimationFrame---
addMessageChannel---
useEffect---
setTimeout---
requestIdleCallback---
reactjs scheduler event-loop
1个回答
4
投票

2024年更新

规范刚刚被重写,以便窗口事件循环的更新渲染步骤实际上是从任务中排队的。
所以现在可以说,动画帧回调是从任务调用的,尽管它们仍然是回调,并且每个回调与之前和之后的其他回调之间仍然存在微任务。虽然规范没有评估这一点,但更新渲染任务在事件循环中仍然占有非常特殊的位置,它有自己的渲染任务源。 现在回到原来的(修改后的)答案。


我不确定

useEffect
所以我相信你的话,他们使用 MessageChannel 并认为
addMessageChannel
useEffect
是平局。

首先是标题(至少部分):

[]requestAnimationFrame属于microtask还是macrotask[...]?

从技术上讲...都不是(参见标题“2024 更新”部分)

requestAnimationFrame
(rAF) 的回调是...回调。
友情提醒,不存在“宏任务”这样的东西:有“tasks”和“microtasks”,后者是前者的子集。
现在,虽然微任务是任务,但它们确实有一个特殊的处理模型,因为它们确实有自己的微任务队列(不是任务队列),并且在每个事件循环迭代期间将被访问多次。 事件循环处理模型中定义了多个“微任务检查点”,每次 JS 调用堆栈为空时,该微任务队列也会被访问。
还有一些任务,通俗地称为“宏任务”,以区别于微任务。每个事件循环迭代只会执行其中一个任务,在第一步中选择。
最后还有回调。这些可以从任务中调用(例如,当任务要触发事件时),或者在某些特定的事件循环迭代中调用,称为“绘画帧”。
事实上,标记为“更新渲染”的步骤偶尔会被调用一次(通常是在显示器发送垂直同步更新时),并且会运行一系列操作,调用回调,其中包括我们亲爱的 rAF 的回调。 为什么这很重要?因为这意味着 rAF(以及“绘画框架”中的其他回调)在事件循环中具有特殊的位置,它们似乎以最高优先级被调用。实际上,它们本身并不参与任务优先级系统(这发生在事件循环的第一步),它们确实可以从相同的事件循环迭代中调用,甚至可以从对它们进行排队的任务中调用。

setTimeout(() => { console.log("timeout 1"); requestAnimationFrame(() => console.log("rAF callback")); const now = performance.now(); while(performance.now() - now < 1000) {} // lock the event loop }); setTimeout(() => console.log("timeout 2"));


我们可以将其与其他片段进行比较,我们从 rAF 回调内部开始整个事情:

requestAnimationFrame(() => { setTimeout(() => { console.log("timeout 1"); requestAnimationFrame(() => console.log("rAF callback")); }); setTimeout(() => console.log("timeout 2")); });

虽然这看起来像是在绘画框架中调用我们的任务的特殊情况,但实际上很常见,因为

浏览器

最近决定打破rAF使对rAF的第一次调用立即触发绘画框架当文档没有动画时。 因此,任何使用 rAF 的测试都应该在文档启动很久之后开始,并且 rAF 循环已经在后台运行......

好的,所以 rAF 结果可能是不确定的。你的其他结果呢?

首先承诺,是的。也不是任务优先级的一部分,如上所述,一旦 JS 调用堆栈为空,微任务队列就会被访问,作为运行脚本后
    clean步骤的一部分。
  • rAF,随机。
  • addMessageChannel
  • ,请参阅我的
  • 这个答案
    。基本上,在 Chrome 中,这是因为 
    setTimeout 的最小超时为 1ms
    ,并且消息任务源比超时任务源具有更高的优先级。
    setTimeout
  • 过去在 Chrome 中具有 1ms 的最小延迟,并且优先级低于消息事件,但在消息之前调用它仍然不会违反规范。
  • requestIdleCallback
  • ,这个有点复杂,但考虑到它会等待事件循环在一段时间内没有做任何事情,所以它将是最后一个。
  •     
© www.soinside.com 2019 - 2024. All rights reserved.