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

标签 reactjs scheduler event-loop

如何 react 日程影响?我做了一些测试,似乎 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---

最佳答案

我不确定 useEffect 所以我会相信你的话,他们使用 MessageChannel 并考虑 addMessageChanneluseEffect 平局。

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

[Does] requestAnimationFrame belong to microtask or macrotask[...]?

从技术上讲……两者都不是。 requestAnimationFrame (rAF) 的回调是...回调。
友情提醒,不存在“宏任务”这样的东西:有“tasks”和“microtasks”,后者是前者的子集。
现在,虽然微任务是任务,但它们确实有一个特殊的处理模型,因为它们确实有自己的微任务队列(不是任务队列),并且在每个事件循环迭代期间将被访问多次。有多个"microtask-checkpoints" event-loop processing model中定义,并且每次 JS 调用堆栈为空时,该微任务队列也会被访问。
还有一些任务,通俗地称为“宏任务”,以区别于微任务。每个 event-loop iteration 只会执行其中一个任务,在第一步中选择。
最后还有回调。这些可以从任务中调用(例如,当任务要触发事件时),或者在某些特定的事件循环迭代中调用,称为“绘画框架”。
事实上标记为 update the rendering 的步骤偶尔会被调用一次(一般是在显示器发送垂直同步更新时),并且会运行一系列操作,调用回调,其中就有我们亲爱的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"));
});

虽然这看起来像是在绘画框架中调用我们的任务的特殊情况,但实际上很常见,因为 browsers have recently决定中断 rAF,当文档没有动画时,对 rAF 的第一次调用立即触发绘画框架。
因此,任何使用 rAF 的测试都应该在文档启动很久之后开始,并且 rAF 循环已经在后台运行...


好的,所以 rAF 结果可能是错误的。你的其他结果呢?

  • 首先 promise ,是的。也不是任务优先级的一部分,如上所述,一旦 JS 调用堆栈为空,微任务队列就会被访问,作为 clean after running a script 的一部分步骤。
  • rAF,侥幸。
  • addMessageChannel,请参阅this answer我的。基本上,在 Chrome 中,这是由于 setTimeout 的最小超时为 1ms,并且消息任务源的优先级高于超时任务源。
  • setTimeout 目前在 Chrome 中具有 1 毫秒的最小延迟,并且优先级低于 MessageEvents,但在消息之前调用它仍然不会违反规范。
  • requestIdleCallback,这个有点复杂,但考虑到它将等待事件循环在一段时间内没有执行任何操作,因此它将是最后一个。

关于reactjs - requestAnimationFrame在主线程任务管理中属于microtask还是macrotask?如果不是,我们如何对这种渲染端任务进行分类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70995372/

相关文章:

javascript - 如何声明 useState() 初始值为 null,然后再给它一个对象值?

linux - 在 linux scheduler 中,它是否跟踪当前正在休眠的任务或已终止的任务?

javascript - JavaScript 中的事件循环和 Node.js 中的异步非阻塞 I/O 有什么区别?

python - 我如何 "wake up"事件循环来通知它另一个线程已完成 Future ?

javascript - 为什么 setTimeout(0) 和 setImmediate() 的行为在主模块中使用时未定义?

javascript - 无法访问传递给组件 React 的对象

javascript - 在 React.js 中实现阅读更多链接

javascript - 我应该检查哪种 Prop 类型以获取来源?

python - Airflow :将 {{ ds }} 作为参数传递给 PostgresOperator

java - 如何获取给定作业名称和组名称的 cron 表达式?