• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

竹千代

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

React Fiber的时间分片调度逻辑

一、大概代码

function workLoop(deadline) {

  let shouldYield = false;

  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    shouldYield = deadline.timeRemaining() < 1;  // timeRemaining是一个实时计算当前桢剩余时间的函数
  }

  if (nextUnitOfWork) {
    requestIdleCallback(workLoop); // 还有任务就下一帧继续
  } else {
    commitRoot(); // 所有任务完成,提交更新
  }
}

requestIdleCallback(workLoop);

// requestIdleCallback的回调函数入参deadline有2个重要属性: timeRemaining():返回当前帧剩余的毫秒数,React 会用它判断还能不能继续执行任务; didTimeout:表示任务是否已经超时(有超时时间时设置)

 

二、宏任务和微任务

类型举例执行时机
宏任务(macrotask) setTimeout, setInterval, setImmediate, MessageChannel, UI 事件 一轮事件循环的起点
微任务(microtask) Promise.then, MutationObserver, queueMicrotask 当前宏任务执行完之后立即
  • 宏任务驱动事件循环,每一轮宏任务是整个事件循环的主角

  • 微任务只是挂在当前宏任务后执行(所以同一循环的宏任务和微任务,微任务先执行,因为微任务在本次事件循环结束前执行;宏任务是下一次事件循环中执行)

“帧刷新”与“事件循环”不完全同步
有可能一次帧里跑多个事件循环(宏任务 + 微任务),也可能一次事件循环跨多帧(如果任务执行太久)

三、仿照实现

let deadline = 0;
      const frameInterval = 5; // 时间片:5ms

      // 获取当前时间(更精准)
      function getCurrentTime() {
        return performance.now();
      }

      function shouldYield() {
        return getCurrentTime() >= deadline;
      }

      // 模拟大任务:遍历很多数字
      function runBigTask(callback) {
        let i = 0;
        const total = 10000;

        function workLoop() {
          deadline = getCurrentTime() + frameInterval;

          while (i < total && !shouldYield()) {
            callback(i); // 执行一个任务单元
            i++;
          }

          if (i < total) {
            schedulePerformWork(); // 时间片不够了,下一帧继续
          } else {
            console.log("任务完成");
          }
        }

        schedulePerformWork(workLoop);
      }

      // 使用 MessageChannel 实现精确调度
      let scheduledCallback = null;
      const channel = new MessageChannel();
      channel.port1.onmessage = () => {
        if (scheduledCallback) scheduledCallback();
      };

      function schedulePerformWork(callback) {
        scheduledCallback = callback || scheduledCallback;
        channel.port2.postMessage(null);
      }

      // 点击开始执行
      startBtn.addEventListener("click", () => {
        log.textContent = "";
        runBigTask((i) => {
          if (i % 100 === 0) {
            log.textContent += `处理第 ${i} 项\n`;
          }
        });
      });

 

 

posted on 2025-04-23 07:03  竹千代  阅读(22)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3