React 使用 MessageChannel 实现异步更新

React 通过异步合并更新,提高渲染速度。

RequestIdleCallback 的不足

  • 兼容性差-RequestIdleCallback 并不是对所有浏览器都支持的。
  • 工作帧率低,只有 20FPS,

SetTimeout 是最后的兜底

setTimeout 精确度不够,即使你把延迟时间设置成 0,实际上还会有 4ms 的延迟时间,只能作最后的兜底。

js 复制代码
setTimeout(() => {
    // 执行异步操作
}, 0);

如何使用 MessageChannel?

入口函数 requestHostCallback:

js 复制代码
// 是否有其他异步任务在运行(异步任务指的是 React 创建的异步任务)
// 只有一个主线程,每次只能执行一个任务
// isMessageLoopRunning 类似一把锁,没有任务执行时是 false
// 当要执行一个任务 A 时,isMessageLoopRunning 被设置为 true,防止其他任务执行
// 执行完任务 A,再把 isMessageLoopRunning 设置为 false,才可继续执行任务 B。
let isMessageLoopRunning = false;

function requestHostCallback() {
  if (!isMessageLoopRunning) {
    isMessageLoopRunning = true;
    // 执行 work
    schedulePerformWorkUntilDeadline();
  }
}

使用 MessageChanne 创建宏任务,实现异步任务队列,以实现异步更新,确保 React 在执行更新时能够合并多个更新操作,并在下一个宏任务中一次性更新,以提高性能并减少不必要的重复渲染,从而提高页面性能和用户体验。

js 复制代码
const channel = new MessageChannel();
const port = channel.port2;
// 监听
channel.port1.onmessage = performWorkUntilDeadline;
// 在一个时间切片内执行,直到时间切片结束
function schedulePerformWorkUntilDeadline() {
  // 执行 work,即发送消息
  port.postMessage(null);
}

function performWorkUntilDeadline() {
  if (isMessageLoopRunning) {
    const currentTime = getCurrentTime();
    // 记录了一个 work 的起始时间,其实就是一个时间切片的起始时间,这是个时间戳
    // 是否结束通过 shouldYieldToHost 函数判断
    startTime = currentTime;
    let hasMoreWork = true;
    try {
      hasMoreWork = flushWork(currentTime);
    } finally {
      if (hasMoreWork) {
        // 有更多的任务
        schedulePerformWorkUntilDeadline();
      } else {
        // 没有任务,这轮 work 结束
        isMessageLoopRunning = false;
      }
    }
  }
}

function flushWork(initialTime: number) {
  // 结束主线程调度
  isHostCallbackScheduled = false;
  // 开始执行任务
  isPerformingWork = true;

  try {
    return workLoop(initialTime);
  } finally {
    currentTask = null;
    // 结束执行任务
    isPerformingWork = false;
  }
}

其中 workLoop 函数和 shouldYieldToHos 函数参考文章 认识 React 中的时间切片。

相关推荐
liangshanbo12151 小时前
面试题:React.memo 和 useMemo 的区别
前端·react.js·前端框架
太子釢1 天前
React 函数组件与 Hook 实践指南
前端·react.js
liangshanbo12151 天前
面试题:React 中 Lane 和 Scheduler 是怎么配合的?
前端·react.js·前端框架
liangshanbo12151 天前
React面试题:Hooks 为什么不能在分支和循环里面写?
前端·javascript·react.js
FungLeo1 天前
成为全栈·Next.js 网站前台篇·响应式、可访问性与错误状态:内容站不能只验桌面首页
react.js·响应式设计·next.js·移动端适配·成为全栈·web 可访问性
ss2731 天前
AI全栈实战 | 3.5-01 Python 全栈前端最小集:不会 React 也能做出能看的后台,关键就一条线
前端·python·react.js
记得开心一点嘛2 天前
Trellora:基于 Electron、React 和本地知识库的 AI 知识工作台
人工智能·react.js·electron
500842 天前
React Native for OpenHarmony 实战:三方库 react-native-url-polyfill 的鸿蒙化适配指南
javascript·react native·react.js·性能优化·electron·harmonyos
500842 天前
React Native for OpenHarmony 实战:三方库 react-native-volume-control 的鸿蒙化适配指南
javascript·react native·react.js·electron·harmonyos
薛一半2 天前
React-Redux三重优化实战揭秘
javascript·vue.js·react.js