React-MessageChannel是怎么模拟requestIdleCallback的?

686 阅读2分钟

MessageChannel 介绍

React中 使用 MessageChannel 来模拟 requestIdleCallback

requestIdleCallback 是浏览器的 api 方法,这个函数将在浏览器空闲时期被调用 var handle = window.requestIdleCallback(callback[, options])

-   callback: 一个在事件循环空闲时即将被调用的函数的引用。函数会接收到一个名为 IdleDeadline 的参数,这个参数可以获取当前空闲时间以及回调是否在超时时间前已经执行的状态。
-   MessageChannel 允许我们创建一个新的消息通道,并通过它的两个 MessagePort (port1 和 port2)属性发送数据。

在 React 应用程序中,多个任务需要同时被执行,例如渲染组件、处理用户输入、更新状态等。如果所有的任务都在同一时间内执行,那么它们之间将会互相干扰,导致应用程序的性能下降和用户体验变差。时间切片是一种将任务分割成小的时间段的技术,这样一来,任务就可以独立运行并分批次处理。

React中的代码实现


const channel = new MessageChannel();
var port2 = channel.port2;
var port1 = channel.port1;
port1.onmessage = performWorkUntilDeadline;

function performWorkUntilDeadline() {
  if (scheduleHostCallback) {
    // 先获取开始执行任务的时间
    //表示时间片的开始
    startTime = getCurrentTime();
    // 是否有更多的工作要做
    let hasMoreWork = true;
    try {
      //执行 flushWork ,并判断有没有返回值
      hasMoreWork = scheduleHostCallback(startTime);
    } finally {
      //执行完以后如果为true,说明还有更多工作要做
      if (hasMoreWork) {
        //继续执行
        schedulePerformWorkUntilDeadline();
      } else {
        scheduleHostCallback = null;
      }
    }

  }
}

function schedulePerformWorkUntilDeadline() {
  port2.postMessage(null);
}

function scheduleCallback(priorityLevel, callback) {
  // 获取当前的时候
  const currentTime = getCurrentTime();
  // 此任务的开时间
  const startTime = currentTime;
  //超时时间
  let timeout;
  //根据优先级计算过期的时间
  switch (priorityLevel) {
    case ImmediatePriority:
      timeout = IMMEDIATE_PRIORITY_TIMEOUT;// -1
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;// 250ms
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT; //1073741823
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT; //10000
      break;
    case NormalPriority:
    default:
      timeout = NORMAL_PRIORITY_TIMEOUT; //5000
      break;
  }
  //计算此任务的过期时间
  const expirationTime = startTime + timeout;
  const newTask = {
    id: taskIdCounter++,
    callback,//回调函数或者说任务函数
    priorityLevel,//优先级别
    startTime,//任务的开始时间
    expirationTime,//任务的过期时间
    sortIndex: expirationTime //排序依赖
  }
  //向任务最小堆里添加任务,排序的依据是过期时间
  push(taskQueue, newTask);
  //flushWork执行工作,刷新工作,执行任务
  requestHostCallback(workLoop);
  return newTask;
}

小结

时间切片的一个重要方面是任务优先级。React 将任务分为5个优先级:ImmediateUserBlockingPriorityIdlePriorityNormalPriorityLowPriority。这些优先级是确定任务完成顺序的关键。