Skip to content

02.调度核心逻辑

scheduler/src/forks/Scheduler.js

js
import { push, peek, pop } from './SchedulerMinHeap';
import {
  ImmediatePriority, UserBlockingPriority, NormalPriority, LowPriority,
  IdlePriority
} from './SchedulerPriorities';

/**
 * 获取当前时间
 * @returns {number} 当前时间,以毫秒为单位
 */
function getCurrentTime() {
  return performance.now();
}

var maxSigned31BitInt = 1073741823;
var IMMEDIATE_PRIORITY_TIMEOUT = -1;
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
var NORMAL_PRIORITY_TIMEOUT = 5000;
var LOW_PRIORITY_TIMEOUT = 10000;
var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
let taskIdCounter = 1;
const taskQueue = [];
let scheduleHostCallback = null;
let startTime = -1;
let currentTask = null;
const frameInterval = 5;

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

/**
 * 调度回调函数
 * @param {ImmediatePriority | UserBlockingPriority | NormalPriority | LowPriority | IdlePriority} priorityLevel - 优先级
 * @param {Function} callback - 要执行的回调函数
 * @returns {Object} 新创建的任务对象
 */
export function scheduleCallback(priorityLevel, callback) {
  const currentTime = getCurrentTime();
  const startTime = currentTime;
  let timeout;
  switch (priorityLevel) {
    case ImmediatePriority:
      timeout = IMMEDIATE_PRIORITY_TIMEOUT;
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT;
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT;
      break;
    case NormalPriority:
    default:
      timeout = NORMAL_PRIORITY_TIMEOUT;
      break;
  }
  const expirationTime = startTime + timeout;
  const newTask = {
    id: taskIdCounter++,
    callback,
    priorityLevel,
    startTime,
    expirationTime,
    sortIndex: expirationTime
  }
  push(taskQueue, newTask);
  requestHostCallback(workLoop);
  return newTask;
}

/**
 * 判断是否应该交还控制权给主机
 * @returns {boolean} 如果应该交还控制权给主机,则返回 true;否则返回 false
 */
function shouldYieldToHost() {
  const timeElapsed = getCurrentTime() - startTime;
  if (timeElapsed < frameInterval) {
    return false;
  }
  return true;
}

/**
 * 工作循环,执行任务队列中的任务
 * @param {number} startTime - 工作循环的开始时间
 * @returns {boolean} 如果还有未完成的任务,返回 true;否则返回 false
 */
function workLoop(startTime) {
  let currentTime = startTime;
  currentTask = peek(taskQueue);
  while (currentTask !== null) {
    if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
      break;
    }
    const callback = currentTask.callback;
    if (typeof callback === 'function') {
      currentTask.callback = null;
      const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
      const continuationCallback = callback(didUserCallbackTimeout);
      if (typeof continuationCallback === 'function') {
        currentTask.callback = continuationCallback;
        return true;
      }
      if (currentTask === peek(taskQueue)) {
        pop(taskQueue);
      }
    } else {
      pop(taskQueue);
    }
    currentTask = peek(taskQueue);
  }
  if (currentTask !== null) {
    return true;
  }
  return false;
}

/**
 * 请求主机回调
 * @param {Function} workLoop - 工作循环函数
 */
function requestHostCallback(workLoop) {
  scheduleHostCallback = workLoop;
  schedulePerformWorkUntilDeadline();
}

/**
 * 安排执行工作直到截止时间
 */
function schedulePerformWorkUntilDeadline() {
  port2.postMessage(null);
}

/**
 * 执行工作直到截止时间
 */
function performWorkUntilDeadline() {
  if (scheduleHostCallback) {
    startTime = getCurrentTime();
    let hasMoreWork = true;
    try {
      hasMoreWork = scheduleHostCallback(startTime);
    } finally {
      if (hasMoreWork) {
        schedulePerformWorkUntilDeadline();
      } else {
        scheduleHostCallback = null;
      }
    }
  }
}

export {
  scheduleCallback as unstable_scheduleCallback,
  shouldYieldToHost as unstable_shouldYield,
  ImmediatePriority as unstable_ImmediatePriority,
  UserBlockingPriority as unstable_UserBlockingPriority,
  NormalPriority as unstable_NormalPriority,
  LowPriority as unstable_LowPriority,
  IdlePriority as unstable_IdlePriority
}

react-reconciler/src/Scheduler.js

js
import * as Scheduler from 'scheduler';
export const scheduleCallback = Scheduler.unstable_scheduleCallback;
export const shouldYield = Scheduler.unstable_shouldYield;
export const ImmediatePriority = Scheduler.unstable_ImmediatePriority;
export const UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
export const NormalPriority = Scheduler.unstable_NormalPriority;
export const IdlePriority = Scheduler.unstable_IdlePriority;

react-reconciler/src/ReactFiberWorkLoop.js

diff
import {
  scheduleCallback,
+  NormalPriority as NormalSchedulerPriority,
+  shouldYield,
} from "scheduler";

function ensureRootIsScheduled(root) {
  if (workInProgressRoot) return;
  workInProgressRoot = root;
-scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
+  scheduleCallback(NormalSchedulerPriority, performConcurrentWorkOnRoot.bind(null, root));
}

function commitRoot(root) {
  const { finishedWork } = root;
  if ((finishedWork.subtreeFlags & Passive) !== NoFlags
    || (finishedWork.flags & Passive) !== NoFlags) {
    if (!rootDoesHavePassiveEffect) {
      rootDoesHavePassiveEffect = true;
-     scheduleCallback(flushPassiveEffect);
+     scheduleCallback(NormalSchedulerPriority, flushPassiveEffect);
    }
  }
  ...
}

react-reconciler/src/Scheduler.js

基于 VitePress 构建