紧接着上面的 Fiber Root,我们来看一下非批处理、同步任务、剩余任务做了什么

performWorkOnRoot 非批处理任务

function performWorkOnRoot(
  root: FiberRoot,
  expirationTime: ExpirationTime,
  isYieldy: boolean,
) {
  invariant(
    !isRendering,
    'performWorkOnRoot was called recursively. This error is likely caused ' +
      'by a bug in React. Please file an issue.',
  );
  // 要开始渲染了
  isRendering = true;

  // Check if this is async work or sync/expired work.
  // isYieldy=true 表示异步的
  // 同步任务
  if (!isYieldy) {
    // Flush work without yielding.
    // TODO: Non-yieldy work does not necessarily imply expired work. A renderer
    // may want to perform some work without yielding, but also without
    // requiring the root to complete (by triggering placeholders).
    // update队列
    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      // This root is already complete. We can commit it.
      // 存在任务了
      // finishedWork存在,进行commit了
      completeRoot(root, finishedWork, expirationTime);
    } else {
      root.finishedWork = null;
      // If this root previously suspended, clear its existing timeout, since
      // we're about to try rendering again.
      // 如果这个Root,之前有暂停,清除掉这个定时器,会尝试继续渲染
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
        cancelTimeout(timeoutHandle);
      }
      // 继续调度
      renderRoot(root, isYieldy);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        // We've completed the root. Commit it.
        // 判断是否可以执行commit
        completeRoot(root, finishedWork, expirationTime);
      }
    }
  } else {
    // Flush async work.
    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      // This root is already complete. We can commit it.
      completeRoot(root, finishedWork, expirationTime);
    } else {
      root.finishedWork = null;
      // If this root previously suspended, clear its existing timeout, since
      // we're about to try rendering again.
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
        cancelTimeout(timeoutHandle);
      }
      renderRoot(root, isYieldy);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        // We've completed the root. Check the if we should yield one more time
        // before committing.
        if (!shouldYieldToRenderer()) {
          // Still time left. Commit the root.
          completeRoot(root, finishedWork, expirationTime);
        } else {
          // There's no time left. Mark this root as complete. We'll come
          // back and commit it later.
          root.finishedWork = finishedWork;
        }
      }
    }
  }

  isRendering = false;
}

一进来,将 isRendering 设置为 true,然后判断是否存在 finishedWork,如果存在,执行 completeRoot 方法,开始更新 DOM

function completeRoot(
  root: FiberRoot,
  finishedWork: Fiber,
  expirationTime: ExpirationTime,
): void {
  // Check if there's a batch that matches this expiration time.
  const firstBatch = root.firstBatch;
  if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
    if (completedBatches === null) {
      completedBatches = [firstBatch];
    } else {
      completedBatches.push(firstBatch);
    }
    if (firstBatch._defer) {
      // This root is blocked from committing by a batch. Unschedule it until
      // we receive another update.
      root.finishedWork = finishedWork;
      root.expirationTime = NoWork;
      return;
    }
  }

  // Commit the root.
  root.finishedWork = null;

  // Check if this is a nested update (a sync update scheduled during the
  // commit phase).
  if (root === lastCommittedRootDuringThisBatch) {
    // If the next root is the same as the previous root, this is a nested
    // update. To prevent an infinite loop, increment the nested update count.
    nestedUpdateCount++;
  } else {
    // Reset whenever we switch roots.
    lastCommittedRootDuringThisBatch = root;
    nestedUpdateCount = 0;
  }
  runWithPriority(ImmediatePriority, () => {
    // 把更新渲染到页面上
    commitRoot(root, finishedWork);
  });
}

由于 commitRoot 这个方法比较长,将放到下次内容里说明

如果不存在 finishedWork,判断是否之前有被暂停的任务,如果有,清除定时器,生成 finishedWork,继续渲染,如果没有,还是走 completeRoot 方法,更新 DOm,渲染结束,将 isRendering 设置为 false

performSyncWork 同步任务

function performSyncWork() {
  performWork(Sync, false);
}
function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
  // Keep working on roots until there's no more work, or until there's a higher
  // priority event.

  // nextFlushedRoot = highestPriorityRoot;
  // nextFlushedExpirationTime = highestPriorityWork;
  // 找出优先级最高的root
  findHighestPriorityRoot();
  // (performSyncWork | performAsyncWork ) isYieldy=true 是异步
  if (isYieldy) {
    // 重新计算时间
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;
    // __DEV__
    if (enableUserTimingAPI) {
      const didExpire = nextFlushedExpirationTime > currentRendererTime;
      const timeout = expirationTimeToMs(nextFlushedExpirationTime);
      stopRequestCallbackTimer(didExpire, timeout);
    }

    // 存在任务,任务过期时间不为空闲, 过期时间大于minExpirationTime
    // 要么任务完成, 要么时间用尽
    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      minExpirationTime <= nextFlushedExpirationTime &&
      !(didYield && currentRendererTime > nextFlushedExpirationTime)
    ) {
      performWorkOnRoot(
        nextFlushedRoot,
        nextFlushedExpirationTime,
        currentRendererTime > nextFlushedExpirationTime,
      );
      findHighestPriorityRoot();
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
    }
  } else {
    // 存在任务,任务过期时间不为空闲, 过期时间大于minExpirationTime
    // 要么任务完成, 要么时间用尽
    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      minExpirationTime <= nextFlushedExpirationTime
    ) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
      findHighestPriorityRoot();
    }
  }

  // We're done flushing work. Either we ran out of time in this callback,
  // or there's no more work left with sufficient priority.

  // If we're inside a callback, set this to false since we just completed it.
  if (isYieldy) {
    callbackExpirationTime = NoWork;
    callbackID = null;
  }
  // If there's work left over, schedule a new callback.
  if (nextFlushedExpirationTime !== NoWork) {
    scheduleCallbackWithExpirationTime(
      ((nextFlushedRoot: any): FiberRoot),
      nextFlushedExpirationTime,
    );
  }

  // Clean-up.
  finishRendering();
}

找出优先级最高的 root,递归 root 下所有 Fiber,执行 performWorkOnRoot 方法,其实就是上面的非批处理任务

scheduleCallbackWithExpirationTime 剩余任务

function scheduleCallbackWithExpirationTime(
  root: FiberRoot,
  expirationTime: ExpirationTime,
) {
  // 判断是否还有正在跑的任务
  if (callbackExpirationTime !== NoWork) {
    // A callback is already scheduled. Check its expiration time (timeout).
    // 判断任务优先级, 优先级低的任务直接return
    if (expirationTime < callbackExpirationTime) {
      // Existing callback has sufficient timeout. Exit.
      return;
    } else {
      // 清空之前的callbackID
      if (callbackID !== null) {
        // Existing callback has insufficient timeout. Cancel and schedule a
        // new one.
        // 现有的回调没有足够的超时。 取消并安排一个新的。
        cancelDeferredCallback(callbackID);
      }
    }
    // The request callback timer is already running. Don't start a new one.
  } else {
    startRequestCallbackTimer();
  }
  // 设置新的callback和callbackExiporationTime
  callbackExpirationTime = expirationTime;
  const currentMs = now() - originalStartTimeMs;
  const expirationTimeMs = expirationTimeToMs(expirationTime);
  // 判断是否超时
  const timeout = expirationTimeMs - currentMs;
  // 生成一个 callbackID,用于关闭任务
  // scheduledCallback = callback;
  // 把callBack为performAsyncWork 赋给 scheduledCallback,
  // scheduleDeferredCallback = unstable_scheduleCallback  => requestHostCallback => requestAnimationFrame
  callbackID = scheduleDeferredCallback(performAsyncWork, {timeout});
}

首先判断是否存在正在执行的任务,如果存在,判断当前任务有无足够时间,如果时间足够,继续执行,如果时间不够,调用 cancelDeferredCallback 方法,取消之前的 callbackID,重新生成一个

如果不存在正在执行的任务,调用 scheduleDeferredCallback 方法

0