text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow
   */
  
  import type {Fiber} from './ReactFiber';
  import type {Batch, FiberRoot} from './ReactFiberRoot';
  import type {ExpirationTime} from './ReactFiberExpirationTime';
  import type {Interaction} from 'scheduler/src/Tracing';
  
  import {__interactionsRef, __subscriberRef} from 'scheduler/tracing';
  import {
    invokeGuardedCallback,
    hasCaughtError,
    clearCaughtError,
  } from 'shared/ReactErrorUtils';
  import ReactSharedInternals from 'shared/ReactSharedInternals';
  import ReactStrictModeWarnings from './ReactStrictModeWarnings';
  import {
    NoEffect,
    PerformedWork,
    Placement,
    Update,
    Snapshot,
    PlacementAndUpdate,
    Deletion,
    ContentReset,
    Callback,
    DidCapture,
    Ref,
    Incomplete,
    HostEffectMask,
  } from 'shared/ReactSideEffectTags';
  import {
    HostRoot,
    ClassComponent,
    HostComponent,
    ContextProvider,
    HostPortal,
  } from 'shared/ReactWorkTags';
  import {
    enableSchedulerTracing,
    enableProfilerTimer,
    enableUserTimingAPI,
    replayFailedUnitOfWorkWithInvokeGuardedCallback,
    warnAboutDeprecatedLifecycles,
  } from 'shared/ReactFeatureFlags';
  import getComponentName from 'shared/getComponentName';
  import invariant from 'shared/invariant';
  import warningWithoutStack from 'shared/warningWithoutStack';
  
  import {
    scheduleTimeout,
    cancelTimeout,
    noTimeout,
  } from './ReactFiberHostConfig';
  
  import ReactFiberInstrumentation from './ReactFiberInstrumentation';
  import * as ReactCurrentFiber from './ReactCurrentFiber';
  import {
    now,
    scheduleDeferredCallback,
    cancelDeferredCallback,
    prepareForCommit,
    resetAfterCommit,
  } from './ReactFiberHostConfig';
  import {
    markPendingPriorityLevel,
    markCommittedPriorityLevels,
    markSuspendedPriorityLevel,
    markPingedPriorityLevel,
    hasLowerPriorityWork,
    isPriorityLevelSuspended,
    findEarliestOutstandingPriorityLevel,
    didExpireAtExpirationTime,
  } from './ReactFiberPendingPriority';
  import {
    recordEffect,
    recordScheduleUpdate,
    startRequestCallbackTimer,
    stopRequestCallbackTimer,
    startWorkTimer,
    stopWorkTimer,
    stopFailedWorkTimer,
    startWorkLoopTimer,
    stopWorkLoopTimer,
    startCommitTimer,
    stopCommitTimer,
    startCommitSnapshotEffectsTimer,
    stopCommitSnapshotEffectsTimer,
    startCommitHostEffectsTimer,
    stopCommitHostEffectsTimer,
    startCommitLifeCyclesTimer,
    stopCommitLifeCyclesTimer,
  } from './ReactDebugFiberPerf';
  import {createWorkInProgress, assignFiberPropertiesInDEV} from './ReactFiber';
  import {onCommitRoot} from './ReactFiberDevToolsHook';
  import {
    NoWork,
    Sync,
    Never,
    msToExpirationTime,
    expirationTimeToMs,
    computeAsyncExpiration,
    computeInteractiveExpiration,
  } from './ReactFiberExpirationTime';
  import {ConcurrentMode, ProfileMode, NoContext} from './ReactTypeOfMode';
  import {
    enqueueUpdate,
    resetCurrentlyProcessingQueue,
    ForceUpdate,
    createUpdate,
  } from './ReactUpdateQueue';
  import {createCapturedValue} from './ReactCapturedValue';
  import {
    isContextProvider as isLegacyContextProvider,
    popTopLevelContextObject as popTopLevelLegacyContextObject,
    popContext as popLegacyContext,
  } from './ReactFiberContext';
  import {popProvider, resetContextDependences} from './ReactFiberNewContext';
  import {popHostContext, popHostContainer} from './ReactFiberHostContext';
  import {
    recordCommitTime,
    startProfilerTimer,
    stopProfilerTimerIfRunningAndRecordDelta,
  } from './ReactProfilerTimer';
  import {
    checkThatStackIsEmpty,
    resetStackAfterFatalErrorInDev,
  } from './ReactFiberStack';
  import {beginWork} from './ReactFiberBeginWork';
  import {completeWork} from './ReactFiberCompleteWork';
  import {
    throwException,
    unwindWork,
    unwindInterruptedWork,
    createRootErrorUpdate,
    createClassErrorUpdate,
  } from './ReactFiberUnwindWork';
  import {
    commitBeforeMutationLifeCycles,
    commitResetTextContent,
    commitPlacement,
    commitDeletion,
    commitWork,
    commitLifeCycles,
    commitAttachRef,
    commitDetachRef,
  } from './ReactFiberCommitWork';
  import {Dispatcher} from './ReactFiberDispatcher';
  
  export type Deadline = {
    timeRemaining(): number,
    didTimeout: boolean,
  };
  
  export type Thenable = {
    then(resolve: () => mixed, reject?: () => mixed): mixed,
  };
  
  const {ReactCurrentOwner} = ReactSharedInternals;
  
  let didWarnAboutStateTransition;
  let didWarnSetStateChildContext;
  let warnAboutUpdateOnUnmounted;
  let warnAboutInvalidUpdates;
  
  if (enableSchedulerTracing) {
    // Provide explicit error message when production+profiling bundle of e.g. react-dom
    // is used with production (non-profiling) bundle of schedule/tracing
    invariant(
      __interactionsRef != null && __interactionsRef.current != null,
      'It is not supported to run the profiling version of a renderer (for example, `react-dom/profiling`) ' +
        'without also replacing the `schedule/tracing` module with `schedule/tracing-profiling`. ' +
        'Your bundler might have a setting for aliasing both modules. ' +
        'Learn more at http://fb.me/react-profiling',
    );
  }
  
  if (__DEV__) {
    didWarnAboutStateTransition = false;
    didWarnSetStateChildContext = false;
    const didWarnStateUpdateForUnmountedComponent = {};
  
    warnAboutUpdateOnUnmounted = function(fiber: Fiber) {
      // We show the whole stack but dedupe on the top component's name because
      // the problematic code almost always lies inside that component.
      const componentName = getComponentName(fiber.type) || 'ReactClass';
      if (didWarnStateUpdateForUnmountedComponent[componentName]) {
        return;
      }
      warningWithoutStack(
        false,
        "Can't call setState (or forceUpdate) on an unmounted component. This " +
          'is a no-op, but it indicates a memory leak in your application. To ' +
          'fix, cancel all subscriptions and asynchronous tasks in the ' +
          'componentWillUnmount method.%s',
        ReactCurrentFiber.getStackByFiberInDevAndProd(fiber),
      );
      didWarnStateUpdateForUnmountedComponent[componentName] = true;
    };
  
    warnAboutInvalidUpdates = function(instance: React$Component<any>) {
      switch (ReactCurrentFiber.phase) {
        case 'getChildContext':
          if (didWarnSetStateChildContext) {
            return;
          }
          warningWithoutStack(
            false,
            'setState(...): Cannot call setState() inside getChildContext()',
          );
          didWarnSetStateChildContext = true;
          break;
        case 'render':
          if (didWarnAboutStateTransition) {
            return;
          }
          warningWithoutStack(
            false,
            'Cannot update during an existing state transition (such as within ' +
              '`render`). Render methods should be a pure function of props and state.',
          );
          didWarnAboutStateTransition = true;
          break;
      }
    };
  }
  
  // Used to ensure computeUniqueAsyncExpiration is monotonically increasing.
  let lastUniqueAsyncExpiration: number = 0;
  
  // Represents the expiration time that incoming updates should use. (If this
  // is NoWork, use the default strategy: async updates in async mode, sync
  // updates in sync mode.)
  let expirationContext: ExpirationTime = NoWork;
  
  let isWorking: boolean = false;
  
  // The next work in progress fiber that we're currently working on.
  let nextUnitOfWork: Fiber | null = null;
  let nextRoot: FiberRoot | null = null;
  // The time at which we're currently rendering work.
  let nextRenderExpirationTime: ExpirationTime = NoWork;
  let nextLatestAbsoluteTimeoutMs: number = -1;
  let nextRenderDidError: boolean = false;
  
  // The next fiber with an effect that we're currently committing.
  let nextEffect: Fiber | null = null;
  
  let isCommitting: boolean = false;
  
  let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
  
  // Used for performance tracking.
  let interruptedBy: Fiber | null = null;
  
  let stashedWorkInProgressProperties;
  let replayUnitOfWork;
  let isReplayingFailedUnitOfWork;
  let originalReplayError;
  let rethrowOriginalError;
  if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
    stashedWorkInProgressProperties = null;
    isReplayingFailedUnitOfWork = false;
    originalReplayError = null;
    replayUnitOfWork = (
      failedUnitOfWork: Fiber,
      thrownValue: mixed,
      isYieldy: boolean,
    ) => {
      if (
        thrownValue !== null &&
        typeof thrownValue === 'object' &&
        typeof thrownValue.then === 'function'
      ) {
        // Don't replay promises. Treat everything else like an error.
        // TODO: Need to figure out a different strategy if/when we add
        // support for catching other types.
        return;
      }
  
      // Restore the original state of the work-in-progress
      if (stashedWorkInProgressProperties === null) {
        // This should never happen. Don't throw because this code is DEV-only.
        warningWithoutStack(
          false,
          'Could not replay rendering after an error. This is likely a bug in React. ' +
            'Please file an issue.',
        );
        return;
      }
      assignFiberPropertiesInDEV(
        failedUnitOfWork,
        stashedWorkInProgressProperties,
      );
  
      switch (failedUnitOfWork.tag) {
        case HostRoot:
          popHostContainer(failedUnitOfWork);
          popTopLevelLegacyContextObject(failedUnitOfWork);
          break;
        case HostComponent:
          popHostContext(failedUnitOfWork);
          break;
        case ClassComponent: {
          const Component = failedUnitOfWork.type;
          if (isLegacyContextProvider(Component)) {
            popLegacyContext(failedUnitOfWork);
          }
          break;
        }
        case HostPortal:
          popHostContainer(failedUnitOfWork);
          break;
        case ContextProvider:
          popProvider(failedUnitOfWork);
          break;
      }
      // Replay the begin phase.
      isReplayingFailedUnitOfWork = true;
      originalReplayError = thrownValue;
      invokeGuardedCallback(null, workLoop, null, isYieldy);
      isReplayingFailedUnitOfWork = false;
      originalReplayError = null;
      if (hasCaughtError()) {
        const replayError = clearCaughtError();
        if (replayError != null && thrownValue != null) {
          try {
            // Reading the expando property is intentionally
            // inside `try` because it might be a getter or Proxy.
            if (replayError._suppressLogging) {
              // Also suppress logging for the original error.
              (thrownValue: any)._suppressLogging = true;
            }
          } catch (inner) {
            // Ignore.
          }
        }
      } else {
        // If the begin phase did not fail the second time, set this pointer
        // back to the original value.
        nextUnitOfWork = failedUnitOfWork;
      }
    };
    rethrowOriginalError = () => {
      throw originalReplayError;
    };
  }
  
  function resetStack() { // 中断低优先级任务
    // nextUnitOfWork 表示子树中下一个要更新的节点   nextUnitOfWork !== null 表示之前要执行的更新是一个异步的任务的 但由于时间片不够 把时间交回给浏览器了
    // 如果浏览器空闲了会再继续执行
    if (nextUnitOfWork !== null) {
      let interruptedWork = nextUnitOfWork.return;  
      while (interruptedWork !== null) { // 向上去找所有被打断的任务
        unwindInterruptedWork(interruptedWork); // 一个任务被打断了 可能有已经更新了一部分了，此时打断，为了不让更新错乱，需要把已经更新了的部分退回  再去执行优先级高的任务
        interruptedWork = interruptedWork.return;
      }
    }
  
    if (__DEV__) {
      ReactStrictModeWarnings.discardPendingWarnings();
      checkThatStackIsEmpty();
    }
    // 重置stack 开始新任务
    nextRoot = null;
    nextRenderExpirationTime = NoWork;
    nextLatestAbsoluteTimeoutMs = -1;
    nextRenderDidError = false;
    nextUnitOfWork = null;
  }
  
  // 处理原生对象的 effects
  function commitAllHostEffects() {
    while (nextEffect !== null) {
      if (__DEV__) {
        ReactCurrentFiber.setCurrentFiber(nextEffect);
      }
      recordEffect();
  
      const effectTag = nextEffect.effectTag;
  
      if (effectTag & ContentReset) {
        // ContentReset 有文字节点 我们需要重置他文字的内容
        commitResetTextContent(nextEffect);
      }
  
      if (effectTag & Ref) {
        const current = nextEffect.alternate;
        if (current !== null) {
          // 分离 ref 将ref从之前挂载的地方卸载下来
          commitDetachRef(current);
        }
      }
  
      // The following switch statement is only concerned about placement,
      // updates, and deletions. To avoid needing to add a case for every
      // possible bitmap value, we remove the secondary effects from the
      // effect tag and switch on that value.
  
      // Placement 是这个节点时新插入的
      // Update 是更新这个节点
      // Deletion 是删除这个节点
      let primaryEffectTag = effectTag & (Placement | Update | Deletion); 
      // effectTag 这样于上 (Placement | Update | Deletion)  
      // primaryEffectTag 可能是 (Placement | Update | Deletion) 中的一个 或者 多个
      switch (primaryEffectTag) {
        case Placement: {
          commitPlacement(nextEffect);
          // Clear the "placement" from effect tag so that we know that this is inserted, before
          // any life-cycles like componentDidMount gets called.
          // TODO: findDOMNode doesn't rely on this any more but isMounted
          // does and isMounted is deprecated anyway so we should be able
          // to kill this.
          nextEffect.effectTag &= ~Placement;
          break;
        }
        case PlacementAndUpdate: {
          // 有 Placement 也可能有Update
          // 可能是之前存在的节点 但是和他的兄弟节点进行了位置交换
          // Placement
          commitPlacement(nextEffect);
          // Clear the "placement" from effect tag so that we know that this is inserted, before
          // any life-cycles like componentDidMount gets called.
          nextEffect.effectTag &= ~Placement;
  
          // Update
          const current = nextEffect.alternate;
          commitWork(current, nextEffect);
          break;
        }
        case Update: {
          const current = nextEffect.alternate;
          commitWork(current, nextEffect);
          break;
        }
        case Deletion: {
          commitDeletion(nextEffect);
          break;
        }
      }
      nextEffect = nextEffect.nextEffect;
    }
  
    if (__DEV__) {
      ReactCurrentFiber.resetCurrentFiber();
    }
  }
  
  // 循环 effect 链表
  function commitBeforeMutationLifecycles() {
    while (nextEffect !== null) {
      if (__DEV__) {
        ReactCurrentFiber.setCurrentFiber(nextEffect);
      }
  
      const effectTag = nextEffect.effectTag;
      if (effectTag & Snapshot) {
        recordEffect();
        const current = nextEffect.alternate;
        commitBeforeMutationLifeCycles(current, nextEffect);
      }
  
      // Don't cleanup effects yet;
      // This will be done by commitAllLifeCycles()
      nextEffect = nextEffect.nextEffect;
    }
  
    if (__DEV__) {
      ReactCurrentFiber.resetCurrentFiber();
    }
  }
  
  // 执行所有生命周期函数 和 effect 中的callback
  function commitAllLifeCycles(
    finishedRoot: FiberRoot,
    committedExpirationTime: ExpirationTime,
  ) {
    if (__DEV__) {
      ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
      ReactStrictModeWarnings.flushLegacyContextWarning();
  
      if (warnAboutDeprecatedLifecycles) {
        ReactStrictModeWarnings.flushPendingDeprecationWarnings();
      }
    }
    // 递归处理所有的 effect
    while (nextEffect !== null) {
      const effectTag = nextEffect.effectTag;
  
      if (effectTag & (Update | Callback)) {
        // 有 Updatea 和 Callback 才执行
        recordEffect();
        const current = nextEffect.alternate;
        // 根据不同组件 不同的时期（初次渲染，还是更新）执行不同的生命周期方法
        commitLifeCycles(
          finishedRoot,
          current,
          nextEffect,
          committedExpirationTime,
        );
      }
  
      if (effectTag & Ref) {
        recordEffect();
        // 将真正的更新的节点挂载上去
        commitAttachRef(nextEffect);
      }
  
      const next = nextEffect.nextEffect;
      // Ensure that we clean these up so that we don't accidentally keep them.
      // I'm not actually sure this matters because we can't reset firstEffect
      // and lastEffect since they're on every node, not just the effectful
      // ones. So we have to clean everything as we reuse nodes anyway.
      nextEffect.nextEffect = null;
      // Ensure that we reset the effectTag here so that we can rely on effect
      // tags to reason about the current life-cycle.
      nextEffect = next;
    }
  }
  
  function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {
    return (
      legacyErrorBoundariesThatAlreadyFailed !== null &&
      legacyErrorBoundariesThatAlreadyFailed.has(instance)
    );
  }
  
  function markLegacyErrorBoundaryAsFailed(instance: mixed) {
    if (legacyErrorBoundariesThatAlreadyFailed === null) {
      legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
    } else {
      legacyErrorBoundariesThatAlreadyFailed.add(instance);
    }
  }
  
  function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
    isWorking = true; // 在renderRoot 的过程中也会设置为true   isWorking 代表这正在进行更新的操作
    isCommitting = true;
    startCommitTimer();
  
    invariant(
      root.current !== finishedWork,
      'Cannot commit the same tree as before. This is probably a bug ' +
        'related to the return field. This error is likely caused by a bug ' +
        'in React. Please file an issue.',
    );
  
    const committedExpirationTime = root.pendingCommitExpirationTime;
    invariant(
      committedExpirationTime !== NoWork,
      'Cannot commit an incomplete root. This error is likely caused by a ' +
        'bug in React. Please file an issue.',
    );
    root.pendingCommitExpirationTime = NoWork;
  
    // Update the pending priority levels to account for the work that we are
    // about to commit. This needs to happen before calling the lifecycles, since
    // they may schedule additional updates.
    const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
    const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime; // childExpirationTime 所有子树中优先级最高的任务
    // 根据rootFiber的 expirationTime 和 childExpirationTime 标记这个 commit 的优先级
    const earliestRemainingTimeBeforeCommit =
      updateExpirationTimeBeforeCommit === NoWork ||
      (childExpirationTimeBeforeCommit !== NoWork &&
        childExpirationTimeBeforeCommit < updateExpirationTimeBeforeCommit)
        ? childExpirationTimeBeforeCommit
        : updateExpirationTimeBeforeCommit;
    markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
  
    let prevInteractions: Set<Interaction> = (null: any);
    if (enableSchedulerTracing) {
      // Restore any pending interactions at this point,
      // So that cascading work triggered during the render phase will be accounted for.
      prevInteractions = __interactionsRef.current;
      __interactionsRef.current = root.memoizedInteractions;
    }
  
    // Reset this to null before calling lifecycles
    ReactCurrentOwner.current = null;
  
    let firstEffect;
    if (finishedWork.effectTag > PerformedWork) {
      // 说明这个 finishedWork 自己也是有 更新过的  要将它自己也插入到 更新的链表中
      // A fiber's effect list consists only of its children, not itself. So if
      // the root has an effect, we need to add it to the end of the list. The
      // resulting list is the set that would belong to the root's parent, if
      // it had one; that is, all the effects in the tree including the root.
      if (finishedWork.lastEffect !== null) {
        finishedWork.lastEffect.nextEffect = finishedWork;
        firstEffect = finishedWork.firstEffect;
      } else {
        firstEffect = finishedWork;
      }
    } else {
      // There is no effect on the root.
      firstEffect = finishedWork.firstEffect;
    }
  
    prepareForCommit(root.containerInfo);
  
    // Invoke instances of getSnapshotBeforeUpdate before mutation.
    // 会获取需要第一个处理的effect, 也就是第一个需要我们commit 的 fiber对象
    nextEffect = firstEffect;
    startCommitSnapshotEffectsTimer();
    // 第一个循环
    while (nextEffect !== null) {
      let didError = false;
      let error;
      if (__DEV__) {
        // 这三个循环是整个课 唯一会将 dev 代码的地方
        // invokeGuardedCallback 可以帮我们在开发过程中收集错误  是为了防止与浏览器有关的错误发生
        invokeGuardedCallback(null, commitBeforeMutationLifecycles, null);
        if (hasCaughtError()) {
          didError = true;
          error = clearCaughtError();
        }
      } else {
        try {
          // commitBeforeMutationLifecycles 唯一的作用是调用 ClassComponent 中可能会存在的 getSnapshotBeforeUpdate 这么一个生命周期方法
          // getSnapshotBeforeUpdate render() 之前调用 state 已经更新，  典型场景， 获取render之前的dom状态
          commitBeforeMutationLifecycles();
        } catch (e) {
          didError = true;
          error = e;
        }
      }
      if (didError) {
        invariant(
          nextEffect !== null,
          'Should have next effect. This error is likely caused by a bug ' +
            'in React. Please file an issue.',
        );
        captureCommitPhaseError(nextEffect, error);
        // Clean-up
        if (nextEffect !== null) {
          nextEffect = nextEffect.nextEffect;
        }
      }
    }
    stopCommitSnapshotEffectsTimer();
  
    if (enableProfilerTimer) {
      // Mark the current commit time to be shared by all Profilers in this batch.
      // This enables them to be grouped later.
      recordCommitTime();
    }
  
    // Commit all the side-effects within a tree. We'll do this in two passes.
    // The first pass performs all the host insertions, updates, deletions and
    // ref unmounts.
    nextEffect = firstEffect;
    startCommitHostEffectsTimer();
    // 第二个循环
    while (nextEffect !== null) {
      let didError = false;
      let error;
      if (__DEV__) {
        invokeGuardedCallback(null, commitAllHostEffects, null);
        if (hasCaughtError()) {
          didError = true;
          error = clearCaughtError();
        }
      } else {
        try {
          // commitAllHostEffects 主要操作 需要对dom节点做的内容
          commitAllHostEffects();
        } catch (e) {
          didError = true;
          error = e;
        }
      }
      if (didError) {
        invariant(
          nextEffect !== null,
          'Should have next effect. This error is likely caused by a bug ' +
            'in React. Please file an issue.',
        );
        captureCommitPhaseError(nextEffect, error);
        // Clean-up
        if (nextEffect !== null) {
          nextEffect = nextEffect.nextEffect;
        }
      }
    }
    stopCommitHostEffectsTimer();
  
    resetAfterCommit(root.containerInfo);
  
    // The work-in-progress tree is now the current tree. This must come after
    // the first pass of the commit phase, so that the previous tree is still
    // current during componentWillUnmount, but before the second pass, so that
    // the finished work is current during componentDidMount/Update.
    root.current = finishedWork;
  
    // In the second pass we'll perform all life-cycles and ref callbacks.
    // Life-cycles happen as a separate pass so that all placements, updates,
    // and deletions in the entire tree have already been invoked.
    // This pass also triggers any renderer-specific initial effects.
    nextEffect = firstEffect;
    startCommitLifeCyclesTimer();
    // 第三个循环
    while (nextEffect !== null) {
      let didError = false;
      let error;
      if (__DEV__) {
        invokeGuardedCallback(
          null,
          commitAllLifeCycles,
          null,
          root,
          committedExpirationTime,
        );
        if (hasCaughtError()) {
          didError = true;
          error = clearCaughtError();
        }
      } else {
        try {
          // commitAllLifeCycles 和各种组件， 生命周期的方法 callback 都会在这里面被调用
          commitAllLifeCycles(root, committedExpirationTime);
        } catch (e) {
          didError = true;
          error = e;
        }
      }
      if (didError) {
        invariant(
          nextEffect !== null,
          'Should have next effect. This error is likely caused by a bug ' +
            'in React. Please file an issue.',
        );
        captureCommitPhaseError(nextEffect, error);
        if (nextEffect !== null) {
          nextEffect = nextEffect.nextEffect;
        }
      }
    }
  
    // 三个循环调用完之后  就算commit 阶段已经完成 设置全局变量为false
    isCommitting = false;
    isWorking = false;
    stopCommitLifeCyclesTimer();
    stopCommitTimer();
    // 调用 onCommitRoot
    onCommitRoot(finishedWork.stateNode);
    if (__DEV__ && ReactFiberInstrumentation.debugTool) {
      ReactFiberInstrumentation.debugTool.onCommitWork(finishedWork);
    }
  
    // 这里又会执行一次 关于expirationTime 的判断 为什么呢
    // 因为在执行 ClassComponent 的生命周期方法的时候 可能会产生新的更新， childExpiration 会产生变化
    // 所以放在后面， 如果 ClassComponent 再进行判断
    const updateExpirationTimeAfterCommit = finishedWork.expirationTime;
    const childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
    const earliestRemainingTimeAfterCommit =
      updateExpirationTimeAfterCommit === NoWork ||
      (childExpirationTimeAfterCommit !== NoWork &&
        childExpirationTimeAfterCommit < updateExpirationTimeAfterCommit)
        ? childExpirationTimeAfterCommit
        : updateExpirationTimeAfterCommit;
    if (earliestRemainingTimeAfterCommit === NoWork) {
      // If there's no remaining work, we can clear the set of already failed
      // error boundaries.
      legacyErrorBoundariesThatAlreadyFailed = null;
    }
    // 清掉 finishedWork earliestRemainingTimeAfterCommit 是 finishedWork 包括自己的 expiration 和 childExpirationTime 中相对小的 和 非 NoWork 的expiration
    // 它会变成 新的 root 的 expirationTime
    onCommit(root, earliestRemainingTimeAfterCommit);
  
    if (enableSchedulerTracing) {
      __interactionsRef.current = prevInteractions;
  
      let subscriber;
  
      try {
        subscriber = __subscriberRef.current;
        if (subscriber !== null && root.memoizedInteractions.size > 0) {
          const threadID = computeThreadID(
            committedExpirationTime,
            root.interactionThreadID,
          );
          subscriber.onWorkStopped(root.memoizedInteractions, threadID);
        }
      } catch (error) {
        // It's not safe for commitRoot() to throw.
        // Store the error for now and we'll re-throw in finishRendering().
        if (!hasUnhandledError) {
          hasUnhandledError = true;
          unhandledError = error;
        }
      } finally {
        // Clear completed interactions from the pending Map.
        // Unless the render was suspended or cascading work was scheduled,
        // In which case– leave pending interactions until the subsequent render.
        const pendingInteractionMap = root.pendingInteractionMap;
        pendingInteractionMap.forEach(
          (scheduledInteractions, scheduledExpirationTime) => {
            // Only decrement the pending interaction count if we're done.
            // If there's still work at the current priority,
            // That indicates that we are waiting for suspense data.
            if (
              earliestRemainingTimeAfterCommit === NoWork ||
              scheduledExpirationTime < earliestRemainingTimeAfterCommit
            ) {
              pendingInteractionMap.delete(scheduledExpirationTime);
  
              scheduledInteractions.forEach(interaction => {
                interaction.__count--;
  
                if (subscriber !== null && interaction.__count === 0) {
                  try {
                    subscriber.onInteractionScheduledWorkCompleted(interaction);
                  } catch (error) {
                    // It's not safe for commitRoot() to throw.
                    // Store the error for now and we'll re-throw in finishRendering().
                    if (!hasUnhandledError) {
                      hasUnhandledError = true;
                      unhandledError = error;
                    }
                  }
                }
              });
            }
          },
        );
      }
    }
  }
  
  // 重置childExpirationTime
  function resetChildExpirationTime(
    workInProgress: Fiber,
    renderTime: ExpirationTime, // 当前正在进行更新的优先级 的expirationTime
  ) {
    if (renderTime !== Never && workInProgress.childExpirationTime === Never) {
      // The children of this component are hidden. Don't bubble their
      // expiration times.
      return;
    }
  
    let newChildExpirationTime = NoWork;
  
    // Bubble up the earliest expiration time.
    if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
      // We're in profiling mode.
      // Let's use this same traversal to update the render durations.
      let actualDuration = workInProgress.actualDuration;
      let treeBaseDuration = workInProgress.selfBaseDuration;
  
      // When a fiber is cloned, its actualDuration is reset to 0.
      // This value will only be updated if work is done on the fiber (i.e. it doesn't bailout).
      // When work is done, it should bubble to the parent's actualDuration.
      // If the fiber has not been cloned though, (meaning no work was done),
      // Then this value will reflect the amount of time spent working on a previous render.
      // In that case it should not bubble.
      // We determine whether it was cloned by comparing the child pointer.
      const shouldBubbleActualDurations =
        workInProgress.alternate === null ||
        workInProgress.child !== workInProgress.alternate.child;
  
      let child = workInProgress.child;
      while (child !== null) {
        const childUpdateExpirationTime = child.expirationTime;
        const childChildExpirationTime = child.childExpirationTime;
        if (
          newChildExpirationTime === NoWork ||
          (childUpdateExpirationTime !== NoWork &&
            childUpdateExpirationTime < newChildExpirationTime)
        ) {
          newChildExpirationTime = childUpdateExpirationTime;
        }
        if (
          newChildExpirationTime === NoWork ||
          (childChildExpirationTime !== NoWork &&
            childChildExpirationTime < newChildExpirationTime)
        ) {
          newChildExpirationTime = childChildExpirationTime;
        }
        if (shouldBubbleActualDurations) {
          actualDuration += child.actualDuration;
        }
        treeBaseDuration += child.treeBaseDuration;
        child = child.sibling;
      }
      workInProgress.actualDuration = actualDuration;
      workInProgress.treeBaseDuration = treeBaseDuration;
    } else {
      let child = workInProgress.child;
      // 因为completeUnitOfWork是从下到上的 处理的，
      // 所以 所有的节点都会从下到上给它 赋值childExpirtaionTime
      // 通过循环 在子节点中寻找优先级最高的 expirationTime作为它的childExpirtaionTime
      while (child !== null) {
        const childUpdateExpirationTime = child.expirationTime;
        const childChildExpirationTime = child.childExpirationTime;
        if (
          newChildExpirationTime === NoWork ||
          (childUpdateExpirationTime !== NoWork &&
            childUpdateExpirationTime < newChildExpirationTime)
        ) {
          newChildExpirationTime = childUpdateExpirationTime;
        }
        if (
          newChildExpirationTime === NoWork ||
          (childChildExpirationTime !== NoWork &&
            childChildExpirationTime < newChildExpirationTime)
        ) {
          newChildExpirationTime = childChildExpirationTime;
        }
        child = child.sibling;
      }
    }
  
    workInProgress.childExpirationTime = newChildExpirationTime;
  }
  
  // 建立 fiber节点 的effect链表
  // fiber节点的 firstEffect 和 lastEffect 形成一个需要更新的节点链表（firstEffect 和 lastEffect都是fiber）
  // effect 记录节点信息 以及要更新 的信息 updateQueue 以及effectTag(操作类型)
  // effect 链表上只会记录有sideffect(有改变) 的节点 
  // !!!深度优先递归 一直将 链表 连接到 RootFiber节点上， 
  // !!!在 RootFiber通过 firstEffect  和 lastEffect 形成 整棵树的 effect链表（串联effect 方便进行节点更新）
  function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
    // Attempt to complete the current unit of work, then move to the
    // next sibling. If there are no more siblings, return to the
    // parent fiber.
    while (true) {
      // The current, flushed, state of this fiber is the alternate.
      // Ideally nothing should rely on this, but relying on it here
      // means that we don't need an additional field on the work in
      // progress.
      const current = workInProgress.alternate;
      if (__DEV__) {
        ReactCurrentFiber.setCurrentFiber(workInProgress);
      }
      // 获取父节点 和兄弟节点
      const returnFiber = workInProgress.return;
      const siblingFiber = workInProgress.sibling;
  
      // Incomplete 就是出现错误然后被捕获时添加的tag
      // 是否是抛出报错的节点 执行不同的操作 
      // 执行 completeWork 或 unwindWork
      if ((workInProgress.effectTag & Incomplete) === NoEffect) {
        // This fiber completed. 无错误
        if (enableProfilerTimer) {
          if (workInProgress.mode & ProfileMode) {
            startProfilerTimer(workInProgress);
          }
          // 调用 completeWork 后就会完成这个节点的更新
          nextUnitOfWork = completeWork(
            current,
            workInProgress,
            nextRenderExpirationTime,
          );
  
          if (workInProgress.mode & ProfileMode) {
            // Update render duration assuming we didn't error.
            stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
          }
        } else {
          nextUnitOfWork = completeWork(
            current,
            workInProgress,
            nextRenderExpirationTime,
          );
        }
        stopWorkTimer(workInProgress);
        // 重置 childExpirationTime
        // workInProgress.childExpirationTime = newChildExpirationTime;
        resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
        if (__DEV__) {
          ReactCurrentFiber.resetCurrentFiber();
        }
  
        if (
          returnFiber !== null &&
          // Do not append effects to parents if a sibling failed to complete
          (returnFiber.effectTag & Incomplete) === NoEffect // 对捕获报错的Incomplete节点唯一可以具有的sideEffect 就是didCapture  因为对于有错误的节点时不会正常渲染子节点的
        ) {
          // 构造所有sideEffect 的节点的 链表
          // 用于在commitWork的使用 对这些有sideEffect的节点进行一个commit的操作
          // Append all the effects of the subtree and this fiber onto the effect
          // list of the parent. The completion order of the children affects the
          // side-effect order.
          
          // 这块的操作
          // 要把当前节点effect链表 挂载到父节点的 effect单项链表的最后
          if (returnFiber.firstEffect === null) { 
            // 父节点没有记录任何一个有sideEffect的子节点
            returnFiber.firstEffect = workInProgress.firstEffect;
          }
          if (workInProgress.lastEffect !== null) {
            if (returnFiber.lastEffect !== null) {
              returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
            }
            returnFiber.lastEffect = workInProgress.lastEffect;
          }
  
          // If this fiber had side-effects, we append it AFTER the children's
          // side-effects. We can perform certain side-effects earlier if
          // needed, by doing multiple passes over the effect list. We don't want
          // to schedule our own side-effect on our own list because if end up
          // reusing children we'll schedule this effect onto itself since we're
          // at the end.
  
          // 当前这个节点可能是有副作用的 （有要执行的操作）
          const effectTag = workInProgress.effectTag;
          // Skip both NoWork and PerformedWork tags when creating the effect list.
          // PerformedWork effect is read by React DevTools but shouldn't be committed.
          
          // 如果仅仅只有PerformedWork 说明他不是一个有sideEffect 的节点
          if (effectTag > PerformedWork) {
            // 若是有sideEffect的节点
            // 添加到returnFiber effect链表上
            if (returnFiber.lastEffect !== null) {
              // 父节点上添加过 effect
              returnFiber.lastEffect.nextEffect = workInProgress;
            } else {
              // returnFiber.lastEffect = == null
              // 父节点上第一次 添加 effect
              returnFiber.firstEffect = workInProgress;
            }
            returnFiber.lastEffect = workInProgress;
          }
        }
  
        if (__DEV__ && ReactFiberInstrumentation.debugTool) {
          ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
        }
  
        if (siblingFiber !== null) {
          // If there is more work to do in this returnFiber, do that next.
          // 有兄弟节点返回 
          // 暂： 返回后会在workLoop中继续循环 再执行同样的操作，建立另外一个分支effect链表，最后在RootFiber汇总，形成最后的effect链表）
          return siblingFiber;
        } else if (returnFiber !== null) {
          // If there's no more work in this returnFiber. Complete the returnFiber.
          // 如果没有兄弟节点 就执行retrunFiber 的 comlepteUnitOfWork
          // 循环向上一直找到 RootFiber 建立effect链表
          workInProgress = returnFiber;
          continue;
        } else {
          // We've reached the root.
          return null;
        }
      } else {
        if (workInProgress.mode & ProfileMode) {
          // Record the render duration for the fiber that errored.
          stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
        }
  
        // This fiber did not complete because something threw. Pop values off
        // the stack without entering the complete phase. If this is a boundary,
        // capture values if possible.
        const next = unwindWork(workInProgress, nextRenderExpirationTime);
        // Because this fiber did not complete, don't reset its expiration time.
        if (workInProgress.effectTag & DidCapture) {
          // Restarting an error boundary
          stopFailedWorkTimer(workInProgress);
        } else {
          stopWorkTimer(workInProgress);
        }
  
        if (__DEV__) {
          ReactCurrentFiber.resetCurrentFiber();
        }
  
        if (next !== null) {
          stopWorkTimer(workInProgress);
          if (__DEV__ && ReactFiberInstrumentation.debugTool) {
            ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
          }
  
          if (enableProfilerTimer) {
            // Include the time spent working on failed children before continuing.
            if (next.mode & ProfileMode) {
              let actualDuration = next.actualDuration;
              let child = next.child;
              while (child !== null) {
                actualDuration += child.actualDuration;
                child = child.sibling;
              }
              next.actualDuration = actualDuration;
            }
          }
  
          // If completing this work spawned new work, do that next. We'll come
          // back here again.
          // Since we're restarting, remove anything that is not a host effect
          // from the effect tag.
          // 保留所有非 shouldCapture 和 Incomplete 的 effectTag
          next.effectTag &= HostEffectMask;
          return next;
        }
  
        if (returnFiber !== null) {
          // Mark the parent fiber as incomplete and clear its effect list.
          // 子fiber 中报错，它父组件也会别带上 Incomplete effectTag 也会走unwindWork
          // 然后判断父组件 是否有处理错误的能力， 没有的话是 Incomplete 有则为 shouldCapture
          // 然后找到有能力的， 中间都是 因为都带了Incomplete 所以都是走的unwindWork
          returnFiber.firstEffect = returnFiber.lastEffect = null;
          returnFiber.effectTag |= Incomplete;
        }
  
        if (__DEV__ && ReactFiberInstrumentation.debugTool) {
          ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
        }
  
        if (siblingFiber !== null) {
          // If there is more work to do in this returnFiber, do that next.
          return siblingFiber;
        } else if (returnFiber !== null) {
          // If there's no more work in this returnFiber. Complete the returnFiber.
          workInProgress = returnFiber;
          continue;
        } else {
          return null;
        }
      }
    }
  
    // Without this explicit null return Flow complains of invalid return type
    // TODO Remove the above while(true) loop
    // eslint-disable-next-line no-unreachable
    return null;
  }
  
  function performUnitOfWork(workInProgress: Fiber): Fiber | null {
    // The current, flushed, state of this fiber is the alternate.
    // Ideally nothing should rely on this, but relying on it here
    // means that we don't need an additional field on the work in
    // progress.
    const current = workInProgress.alternate;
  
    // See if beginning this work spawns more work.
    startWorkTimer(workInProgress);
    if (__DEV__) {
      ReactCurrentFiber.setCurrentFiber(workInProgress);
    }
  
    if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
      stashedWorkInProgressProperties = assignFiberPropertiesInDEV(
        stashedWorkInProgressProperties,
        workInProgress,
      );
    }
  
    let next; // 设置next变量
    if (enableProfilerTimer) {
      if (workInProgress.mode & ProfileMode) {
        startProfilerTimer(workInProgress);
      }
      // beginWork 涉及了对每一个节点的更新! 执行完之后返回它的子节点
      next = beginWork(current, workInProgress, nextRenderExpirationTime);
      // memoizedProps 上一次渲染完成之后的props 等于正在用的props
      workInProgress.memoizedProps = workInProgress.pendingProps;
  
      if (workInProgress.mode & ProfileMode) {
        // Record the render duration assuming we didn't bailout (or error).
        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
      }
    } else {
      next = beginWork(current, workInProgress, nextRenderExpirationTime);
      workInProgress.memoizedProps = workInProgress.pendingProps;
    }
  
    if (__DEV__) {
      ReactCurrentFiber.resetCurrentFiber();
      if (isReplayingFailedUnitOfWork) {
        // Currently replaying a failed unit of work. This should be unreachable,
        // because the render phase is meant to be idempotent, and it should
        // have thrown again. Since it didn't, rethrow the original error, so
        // React's internal stack is not misaligned.
        rethrowOriginalError();
      }
    }
    if (__DEV__ && ReactFiberInstrumentation.debugTool) {
      ReactFiberInstrumentation.debugTool.onBeginWork(workInProgress);
    }
  
    if (next === null) {
      // next === null 说明已经更新到一棵子树的叶子节点了，  说明这棵子树可以结束了
      // If this doesn't spawn new work, complete the current work.
      // completeUnitOfWork 向上遍历找到第一个兄弟节点（一直到最上面rootfiber的兄弟节点吗？）
      next = completeUnitOfWork(workInProgress);
    }
  
    ReactCurrentOwner.current = null;
  
    return next;
  }
  
  function workLoop(isYieldy) {
    // isYieldy：是否可以被中断  sync 和 已经超时的任务是不可以中断的
    if (!isYieldy) { // 不可以中断
      // Flush work without yielding
      while (nextUnitOfWork !== null) { // nextUnitOfWork 是 FiberRoot节点时 它的return是null 这时候回跳出循环
        // performUnitOfWork 继续对单元进行更新  第一个nextUnitOfWork 是 FiberRoot 进行深度优先遍历
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
      }
    } else {
      // Flush asynchronous work until the deadline runs out of time.
      // 判断当前时间片是否还有足够的时间
      while (nextUnitOfWork !== null && !shouldYield()) {
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
      }
    }
  }
  
  function renderRoot(
    root: FiberRoot,
    isYieldy: boolean,
    isExpired: boolean,
  ): void {
    invariant(
      !isWorking,
      'renderRoot was called recursively. This error is likely caused ' +
        'by a bug in React. Please file an issue.',
    );
    isWorking = true;
    ReactCurrentOwner.currentDispatcher = Dispatcher;
  
    const expirationTime = root.nextExpirationTimeToWorkOn;
  
    // Check if we're starting from a fresh stack, or if we're resuming from
    // previously yielded work.
    if (
      expirationTime !== nextRenderExpirationTime ||
      root !== nextRoot ||
      nextUnitOfWork === null
    ) {
      // 接受到root 和 即将（之前）要执行的root一样 可能是之前的异步任务 被刚接受到的高优先级任务打断
      // Reset the stack and start working from the root.
      resetStack();
      nextRoot = root;
      nextRenderExpirationTime = expirationTime; // nextRenderExpirationTime 是一个公共变量 就会在其他地方使用
      nextUnitOfWork = createWorkInProgress(
        nextRoot.current,
        null,
        nextRenderExpirationTime,
      );
      // 拷贝一份到 WorkInProgress对象上 会和current 来回互相替换
      // 在renderRoot之后 真正进行的节点操作都是在WorkInProgress上
      root.pendingCommitExpirationTime = NoWork;
  
      if (enableSchedulerTracing) {
        // Determine which interactions this batch of work currently includes,
        // So that we can accurately attribute time spent working on it,
        // And so that cascading work triggered during the render phase will be associated with it.
        const interactions: Set<Interaction> = new Set();
        root.pendingInteractionMap.forEach(
          (scheduledInteractions, scheduledExpirationTime) => {
            if (scheduledExpirationTime <= expirationTime) {
              scheduledInteractions.forEach(interaction =>
                interactions.add(interaction),
              );
            }
          },
        );
  
        // Store the current set of interactions on the FiberRoot for a few reasons:
        // We can re-use it in hot functions like renderRoot() without having to recalculate it.
        // We will also use it in commitWork() to pass to any Profiler onRender() hooks.
        // This also provides DevTools with a way to access it when the onCommitRoot() hook is called.
        root.memoizedInteractions = interactions;
  
        if (interactions.size > 0) {
          const subscriber = __subscriberRef.current;
          if (subscriber !== null) {
            const threadID = computeThreadID(
              expirationTime,
              root.interactionThreadID,
            );
            try {
              subscriber.onWorkStarted(interactions, threadID);
            } catch (error) {
              // Work thrown by an interaction tracing subscriber should be rethrown,
              // But only once it's safe (to avoid leaveing the scheduler in an invalid state).
              // Store the error for now and we'll re-throw in finishRendering().
              if (!hasUnhandledError) {
                hasUnhandledError = true;
                unhandledError = error;
              }
            }
          }
        }
      }
    }
  
    let prevInteractions: Set<Interaction> = (null: any);
    if (enableSchedulerTracing) {
      // We're about to start new traced work.
      // Restore pending interactions so cascading work triggered during the render phase will be accounted for.
      prevInteractions = __interactionsRef.current;
      __interactionsRef.current = root.memoizedInteractions;
    }
  
    let didFatal = false;
  
    startWorkLoopTimer(nextUnitOfWork);
  
    do {
      try {
        workLoop(isYieldy);
      } catch (thrownValue) {
        if (nextUnitOfWork === null) {
          // This is a fatal error.
          // 正常情况是不可能出现 nextUnitOfWork是 null 
          didFatal = true;
          // 抛出一个无法处理的错误
          onUncaughtError(thrownValue);
        } else {
          if (__DEV__) {
            // Reset global debug state
            // We assume this is defined in DEV
            (resetCurrentlyProcessingQueue: any)();
          }
  
          const failedUnitOfWork: Fiber = nextUnitOfWork;
          if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
            replayUnitOfWork(failedUnitOfWork, thrownValue, isYieldy);
          }
  
          // TODO: we already know this isn't true in some cases.
          // At least this shows a nicer error message until we figure out the cause.
          // https://github.com/facebook/react/issues/12449#issuecomment-386727431
          invariant(
            nextUnitOfWork !== null,
            'Failed to replay rendering after an error. This ' +
              'is likely caused by a bug in React. Please file an issue ' +
              'with a reproducing case to help us find it.',
          );
  
          const sourceFiber: Fiber = nextUnitOfWork;
          let returnFiber = sourceFiber.return;
          if (returnFiber === null) {
            // 在跟新rootFiber 的时候报错说明是 是源码级的错误
            // This is the root. The root could capture its own errors. However,
            // we don't know if it errors before or after we pushed the host
            // context. This information is needed to avoid a stack mismatch.
            // Because we're not sure, treat this as a fatal error. We could track
            // which phase it fails in, but doesn't seem worth it. At least
            // for now.
            didFatal = true;
            onUncaughtError(thrownValue);
          } else {
            // 常规抛出错误
            throwException(
              root,
              returnFiber,
              sourceFiber,
              thrownValue,
              nextRenderExpirationTime,
            );
            // 节点报错了 就直接执行completeUnitOfWork 那时候 自然会走 unwindWork 方法
            nextUnitOfWork = completeUnitOfWork(sourceFiber);
            continue;
          }
        }
      }
      break;
    } while (true);
  
    if (enableSchedulerTracing) {
      // Traced work is done for now; restore the previous interactions.
      __interactionsRef.current = prevInteractions;
    }
  
    // We're done performing work. Time to clean up.
    isWorking = false;
    ReactCurrentOwner.currentDispatcher = null;
    resetContextDependences();
  
    // Yield back to main thread.
    // didFatal 是有致命错误的时候
    if (didFatal) {
      const didCompleteRoot = false;
      stopWorkLoopTimer(interruptedBy, didCompleteRoot);
      interruptedBy = null;
      // There was a fatal error.
      if (__DEV__) {
        resetStackAfterFatalErrorInDev();
      }
      // `nextRoot` points to the in-progress root. A non-null value indicates
      // that we're in the middle of an async render. Set it to null to indicate
      // there's no more work to be done in the current batch.
      nextRoot = null;
      // onFatal  root.finishedWork = null;  直接清空 finishedWork
      onFatal(root);
      return;
    }
  
    if (nextUnitOfWork !== null) { // 正常流程走完nextUnitOfWork是等于null的  时间片不够走完 nextUnitOfWork !== null
      // There's still remaining async work in this tree, but we ran out of time
      // in the current frame. Yield back to the renderer. Unless we're
      // interrupted by a higher priority update, we'll continue later from where
      // we left off.
      // 通过 reactSchdeuler 进行调度，如果 这个root的更新 比较长，
      // 一个时间片没更新完这个root， 那么中断这次更新
      const didCompleteRoot = false;
      stopWorkLoopTimer(interruptedBy, didCompleteRoot);
      interruptedBy = null;
      // 没更新完就onYield root  当下次时间片进来的时候， 再次在这个nextUnitOfWork上进行更新
      // 这个是中断 再继续的流程
      onYield(root);
      return;
    }
  
    // We completed the whole tree.
    const didCompleteRoot = true;
    stopWorkLoopTimer(interruptedBy, didCompleteRoot);
    const rootWorkInProgress = root.current.alternate;
    invariant(
      rootWorkInProgress !== null,
      'Finished root should have a work-in-progress. This error is likely ' +
        'caused by a bug in React. Please file an issue.',
    );
  
    // `nextRoot` points to the in-progress root. A non-null value indicates
    // that we're in the middle of an async render. Set it to null to indicate
    // there's no more work to be done in the current batch.
    nextRoot = null;
    interruptedBy = null;
    // nextRenderDidError和 suspend 有关的地方
    if (nextRenderDidError) {
      // render 是didError了
      // There was an error
      if (hasLowerPriorityWork(root, expirationTime)) {
        // hasLowerPriorityWork  如果有低优先级的任务还没有被更新
        // 如果有低优先级的任务 就不提交这次更新， 
        // 但是 在current 上面的updateQueue是没有被删除的  这些update依然存在
        // 在下次低优先级的任务执行更新时候 依旧会更新那些update 就有可能修复在这此更新过程中产生的问题
  
        // 渲染过程中产生 错误 并且有低优先级的任务， 那么不提交这次更新，  而是把提交的更新放在低优先级任务上 再去渲染一次
        // There's lower priority work. If so, it may have the effect of fixing
        // the exception that was just thrown. Exit without committing. This is
        // similar to a suspend, but without a timeout because we're not waiting
        // for a promise to resolve. React will restart at the lower
        // priority level.
        // markSuspendedPriorityLevel  把当前的更新的任务 给它 suspend 并把这次更新 打上标记
        markSuspendedPriorityLevel(root, expirationTime);
        const suspendedExpirationTime = expirationTime;
        const rootExpirationTime = root.expirationTime;
        // 什么都没做 也就是在这个 renderRoot 中不提交更新（commit）
        onSuspend(
          root,
          rootWorkInProgress,
          suspendedExpirationTime,
          rootExpirationTime,
          -1, // Indicates no timeout
        );
        return;
      } else if (
        // There's no lower priority work, but we're rendering asynchronously.
        // Synchronsouly attempt to render the same level one more time. This is
        // similar to a suspend, but without a timeout because we're not waiting
        // for a promise to resolve.
        !root.didError &&
        !isExpired
      ) {
        // isExpired 是否超时  
        // 如果时间片不够了 是不能发起一个同步的更新的
        // 符合条件 直接发起一个新的同步更新
        root.didError = true;
        // expirationTime 就是 renderRoot 中的  root.nextExpirationTimeToWorkOn
        const suspendedExpirationTime = (root.nextExpirationTimeToWorkOn = expirationTime);
        // 走sync的更新 而不走时间片调度更新   
        // 强制进行同步更新 和渲染的过程
        const rootExpirationTime = (root.expirationTime = Sync);
        // 传入 -1 还是什么都不做
        // 在 调用用 renderRoot 的 方法 performWorkOnRoot 再往上的 performWork 中
        // 知道 在执行完 performWorkOnRoot 后会再寻找 优先级最高 的 更新
        // 上面 已经 将这个 root的 更新 变为同步， 会再更新这些操作， 
        // 但是 因为没有执行 commit 所以下次更新 的 root.current 还是当前的 workInProgress  会从老的状态上重新新发起一次更新
        // 任务挂起就是这样的含义
        onSuspend(
          root,
          rootWorkInProgress,
          suspendedExpirationTime,
          rootExpirationTime,
          -1, // Indicates no timeout
        );
        return;
      }
    }
  
    // nextLatestAbsoluteTimeoutMs !== -1 也会执行一个 onSuspend
    // 这里也是  suspend 的一部分
    if (!isExpired && nextLatestAbsoluteTimeoutMs !== -1) {
      // The tree was suspended.
      const suspendedExpirationTime = expirationTime;
      // markSuspendedPriorityLevel  把当前的更新的任务 给它 suspend 并把这次更新 打上标记 将任务挂起
      markSuspendedPriorityLevel(root, suspendedExpirationTime);
  
      // Find the earliest uncommitted expiration time in the tree, including
      // work that is suspended. The timeout threshold cannot be longer than
      // the overall expiration.
      const earliestExpirationTime = findEarliestOutstandingPriorityLevel(
        root,
        expirationTime,
      );
      // 获取这个 root 中所有更新 优先级最高的那个
      const earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
      if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {
        // 如果 在 最新的更新前 还没执行 timeout 就让它 在这个更新的时候执行掉
        nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;
      }
  
      // Subtract the current time from the absolute timeout to get the number
      // of milliseconds until the timeout. In other words, convert an absolute
      // timestamp to a relative time. This is the value that is passed
      // to `setTimeout`.
      const currentTimeMs = expirationTimeToMs(requestCurrentTime());
      let msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;
      // 计算 msUntilTimeout
      msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;
  
      // TODO: Account for the Just Noticeable Difference
  
      const rootExpirationTime = root.expirationTime;
      // 执行 onSuspend
      onSuspend(
        root,
        rootWorkInProgress,
        suspendedExpirationTime,
        rootExpirationTime,
        msUntilTimeout,
      );
      return;
    }
  
    // Ready to commit.
    onComplete(root, rootWorkInProgress, expirationTime);
  }
  
  function dispatch(
    sourceFiber: Fiber,
    value: mixed,
    expirationTime: ExpirationTime,
  ) {
    invariant(
      !isWorking || isCommitting,
      'dispatch: Cannot dispatch during the render phase.',
    );
  
    let fiber = sourceFiber.return;
    while (fiber !== null) {
      switch (fiber.tag) {
        case ClassComponent:
          const ctor = fiber.type;
          const instance = fiber.stateNode;
          if (
            typeof ctor.getDerivedStateFromError === 'function' ||
            (typeof instance.componentDidCatch === 'function' &&
              !isAlreadyFailedLegacyErrorBoundary(instance))
          ) {
            const errorInfo = createCapturedValue(value, sourceFiber);
            const update = createClassErrorUpdate(
              fiber,
              errorInfo,
              expirationTime,
            );
            enqueueUpdate(fiber, update);
            scheduleWork(fiber, expirationTime);
            return;
          }
          break;
        case HostRoot: {
          const errorInfo = createCapturedValue(value, sourceFiber);
          const update = createRootErrorUpdate(fiber, errorInfo, expirationTime);
          enqueueUpdate(fiber, update);
          scheduleWork(fiber, expirationTime);
          return;
        }
      }
      fiber = fiber.return;
    }
  
    if (sourceFiber.tag === HostRoot) {
      // Error was thrown at the root. There is no parent, so the root
      // itself should capture it.
      const rootFiber = sourceFiber;
      const errorInfo = createCapturedValue(value, rootFiber);
      const update = createRootErrorUpdate(rootFiber, errorInfo, expirationTime);
      enqueueUpdate(rootFiber, update);
      scheduleWork(rootFiber, expirationTime);
    }
  }
  
  function captureCommitPhaseError(fiber: Fiber, error: mixed) {
    return dispatch(fiber, error, Sync);
  }
  
  function computeThreadID(
    expirationTime: ExpirationTime,
    interactionThreadID: number,
  ): number {
    // Interaction threads are unique per root and expiration time.
    return expirationTime * 1000 + interactionThreadID;
  }
  
  // Creates a unique async expiration time.
  function computeUniqueAsyncExpiration(): ExpirationTime {
    const currentTime = requestCurrentTime();
    let result = computeAsyncExpiration(currentTime);
    if (result <= lastUniqueAsyncExpiration) {
      // Since we assume the current time monotonically increases, we only hit
      // this branch when computeUniqueAsyncExpiration is fired multiple times
      // within a 200ms window (or whatever the async bucket size is).
      result = lastUniqueAsyncExpiration + 1;
    }
    lastUniqueAsyncExpiration = result;
    return lastUniqueAsyncExpiration;
  }
  
  function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) { // 计算fiber的expirationTime
    let expirationTime;
    if (expirationContext !== NoWork) {
      // explicit：明确
      // An explicit expiration context was set;
      expirationTime = expirationContext;
    } else if (isWorking) {
      if (isCommitting) {
        // Updates that occur during the commit phase should have sync priority
        // by default.
        expirationTime = Sync;
      } else {
        // Updates during the render phase should expire at the same time as
        // the work that is being rendered.
        expirationTime = nextRenderExpirationTime;
      }
    } else {
      // No explicit expiration context was set, and we're not currently
      // performing work. Calculate a new expiration time.
      if (fiber.mode & ConcurrentMode) {
        if (isBatchingInteractiveUpdates) { // 通过这个属性判断 是调用哪种 expirationTime  事件绑定的时候 isBatchingInteractiveUpdates 是 true
          // This is an interactive update
          expirationTime = computeInteractiveExpiration(currentTime);
        } else {
          // This is an async update
          expirationTime = computeAsyncExpiration(currentTime);
        }
        // If we're in the middle of rendering a tree, do not update at the same
        // expiration time that is already rendering.
        if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
          expirationTime += 1;
        }
      } else {
        // This is a sync update
        expirationTime = Sync;
      }
    }
    if (isBatchingInteractiveUpdates) {
      // This is an interactive update. Keep track of the lowest pending
      // interactive expiration time. This allows us to synchronously flush
      // all interactive updates when needed.
      if (expirationTime > lowestPriorityPendingInteractiveExpirationTime) {
        lowestPriorityPendingInteractiveExpirationTime = expirationTime;
      }
    }
    return expirationTime;
  }
  
  function renderDidSuspend(
    root: FiberRoot,
    absoluteTimeoutMs: number,
    suspendedTime: ExpirationTime,
  ) {
    // Schedule the timeout.
    if (
      absoluteTimeoutMs >= 0 &&
      nextLatestAbsoluteTimeoutMs < absoluteTimeoutMs
    ) {
      nextLatestAbsoluteTimeoutMs = absoluteTimeoutMs;
    }
  }
  
  function renderDidError() {
    nextRenderDidError = true;
  }
  
  // 真实作用就是重新发起一次更新
  function retrySuspendedRoot(
    root: FiberRoot,
    boundaryFiber: Fiber,
    sourceFiber: Fiber,
    suspendedTime: ExpirationTime,
  ) {
    let retryTime;
  
    // 下面这个块其实就是设置 retryTime 的过程
  
    // isPriorityLevelSuspended 判断 promise resolve 的 expirationTime 是否还处于  root 的  suspensedTime 的区间之内
    if (isPriorityLevelSuspended(root, suspendedTime)) {
      // Ping at the original level
      retryTime = suspendedTime;
  
      markPingedPriorityLevel(root, retryTime);
    } else {
      // suspenseTime 超时， 就重新计算一个时间
      // Suspense already timed out. Compute a new expiration time
      const currentTime = requestCurrentTime();
      retryTime = computeExpirationForFiber(currentTime, boundaryFiber);
      // 这里是作为 pendingTime
      markPendingPriorityLevel(root, retryTime);
    }
  
    // TODO: If the suspense fiber has already rendered the primary children
    // without suspending (that is, all of the promises have already resolved),
    // we should not trigger another update here. One case this happens is when
    // we are in sync mode and a single promise is thrown both on initial render
    // and on update; we attach two .then(retrySuspendedRoot) callbacks and each
    // one performs Sync work, rerendering the Suspense.
  
    // 如果是处于 ConcurrentMode 下面
    if ((boundaryFiber.mode & ConcurrentMode) !== NoContext) {
      if (root === nextRoot && nextRenderExpirationTime === suspendedTime) {
        // Received a ping at the same priority level at which we're currently
        // rendering. Restart from the root.
        // 重启root 渲染
        nextRoot = null;
      }
    }
  
    scheduleWorkToRoot(boundaryFiber, retryTime);
    // 不处于 ConcurrentMode 下的处理
    if ((boundaryFiber.mode & ConcurrentMode) === NoContext) {
      // Outside of concurrent mode, we must schedule an update on the source
      // fiber, too, since it already committed in an inconsistent state and
      // therefore does not have any pending work.
      scheduleWorkToRoot(sourceFiber, retryTime);
      const sourceTag = sourceFiber.tag;
      if (sourceTag === ClassComponent && sourceFiber.stateNode !== null) {
        // When we try rendering again, we should not reuse the current fiber,
        // since it's known to be in an inconsistent state. Use a force updte to
        // prevent a bail out.
        // 添加一个 sideEffect  ForceUpdate
        const update = createUpdate(retryTime);
        // update 的tag 是 ForceUpdate 要同步渲染  避免阐释 bail out(退出放弃)
        update.tag = ForceUpdate;
        enqueueUpdate(sourceFiber, update);
      }
    }
  
    const rootExpirationTime = root.expirationTime;
    if (rootExpirationTime !== NoWork) {
      // 再发起更新渲染流程
      requestWork(root, rootExpirationTime);
    }
  }
  // 根据传入的Fiber 节点向上找到对应的 RootFiber对象 并向上更新 childExpirationTime  和 expirtaionTime
  function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {
    recordScheduleUpdate();  // 记录更新流程时间 
  
    if (__DEV__) {
      if (fiber.tag === ClassComponent) {
        const instance = fiber.stateNode;
        warnAboutInvalidUpdates(instance);
      }
    }
  
    // Update the source fiber's expiration time
    if (
      fiber.expirationTime === NoWork || // 这个接点没有任何更新操作
      fiber.expirationTime > expirationTime // expirationTime 是 scheduleWork  传入的 也就是创建update的时候创建的expirationTime 这里 表示之前产生更新的优先级低于现在更新的优先级
    ) {
      fiber.expirationTime = expirationTime;  // 表示之前产生更新的优先级低于现在更新的优先级 那此时这个fiber 会设置成 优先级更高的 expirationTime 
    }
    let alternate = fiber.alternate; // alternate 看下fiber的数据结构 下面这一块是设置alternate(workInProgress) 的expirationTime
    if (
      alternate !== null &&
      (alternate.expirationTime === NoWork ||
        alternate.expirationTime > expirationTime) // 当前没有执行 或者 过期时间  比较大   进行替换 等于是修改成先要执行的expirationTime
    ) {
      alternate.expirationTime = expirationTime;
    }
    // Walk the parent path to the root and update the child expiration time.
    let node = fiber.return;  //  指向他在Fiber节点树中的`parent`，用来在处理完这个节点之后向上返回 向上寻找
    let root = null;  // 只有 RootFiber的 return 是null  说明此时RootFiber
    if (node === null && fiber.tag === HostRoot) {  //  HostRoot 也代表 RootFiber
      root = fiber.stateNode;   //  RootFiber 的 stateNode  是 FiberRoot
    } else { // 假如不是 RootFiber 那就 循环向上一层层寻找
      while (node !== null) {
        alternate = node.alternate;
        if (
          node.childExpirationTime === NoWork ||
          node.childExpirationTime > expirationTime  // 这里的node 是 parent  fiber   而 node.expirtaionTime 是子树种优先级最高的expirtaionTime 比较当前更新 是优先级和node.expirtaionTime
        ) {
          node.childExpirationTime = expirationTime;
          if (
            alternate !== null &&
            (alternate.childExpirationTime === NoWork ||
              alternate.childExpirationTime > expirationTime) // node 的alternate 同样要进行更新
          ) {
            alternate.childExpirationTime = expirationTime;
          }
        } else if ( // 上面是判断 先判断 node.childExpirationTime  这里是要先判断 alternate.childExpirationTime  因为两者不一定相同 
          alternate !== null &&
          (alternate.childExpirationTime === NoWork ||
            alternate.childExpirationTime > expirationTime)
        ) {
          alternate.childExpirationTime = expirationTime;
        }
        if (node.return === null && node.tag === HostRoot) { // 循环 找到 HostRoot 跳出循环 找到 RootFiber
          root = node.stateNode;
          break;
        }
        node = node.return;
      }
    }
  
    if (root === null) { // 假如 tree 没有RootFiber节点是要报错的
      if (__DEV__ && fiber.tag === ClassComponent) {
        warnAboutUpdateOnUnmounted(fiber);
      }
      return null;
    }
  
    if (enableSchedulerTracing) { // 跟profilerMode 去跟踪 引用更新的代码
      const interactions = __interactionsRef.current;
      if (interactions.size > 0) {
        const pendingInteractionMap = root.pendingInteractionMap;
        const pendingInteractions = pendingInteractionMap.get(expirationTime);
        if (pendingInteractions != null) {
          interactions.forEach(interaction => {
            if (!pendingInteractions.has(interaction)) {
              // Update the pending async work count for previously unscheduled interaction.
              interaction.__count++;
            }
  
            pendingInteractions.add(interaction);
          });
        } else {
          pendingInteractionMap.set(expirationTime, new Set(interactions));
  
          // Update the pending async work count for the current interactions.
          interactions.forEach(interaction => {
            interaction.__count++;
          });
        }
  
        const subscriber = __subscriberRef.current;
        if (subscriber !== null) {
          const threadID = computeThreadID(
            expirationTime,
            root.interactionThreadID,
          );
          subscriber.onWorkScheduled(interactions, threadID);
        }
      }
    }
  
    return root;  // 返回了FiberRoot
  }
  
  function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
    const root = scheduleWorkToRoot(fiber, expirationTime); // 返回一个更新了expirationIme和childExpirationTime的root
    if (root === null) {
      return;
    }
  
    if (
      !isWorking &&   // isWorking代表有任务正在渲染当中 
      nextRenderExpirationTime !== NoWork &&  // nextRenderExpirationTime!== NoWork有任务正在进行
      expirationTime < nextRenderExpirationTime  // 新的任务 的优先级 高于 目前正在做的任务
    ) {
      // This is an interruption. (Used for performance tracking.)
      interruptedBy = fiber; // interruptedBy一个记录的值
      resetStack(); //  中断优先级低的任务
    }
    // 因为对于调用scheduleWork 的任务都把它 认为是一个pedding的任务
    // 意思就是等待渲染的任务  这个任务因为没有经过任何的更新， 没有经过 向suspend 这样的流程
    // 所以它是一个新创建的任务  这就是一个pedding的任务
    markPendingPriorityLevel(root, expirationTime); // 和FiberRoot上 earliestSuspendedTime latestSuspendedTime,等 有关
    if (
      // If we're in the render phase, we don't need to schedule this root
      // for an update, because we'll do it before we exit...
      !isWorking || // 没有正在工作   isWorking 包含 了render 和 isCommiting两个阶段
      isCommitting || // 正在提交   isCommitting阶段是不可以打断的  把fiber树整体渲染要将它渲染到dom上的阶段就是isCommitting
      // ...unless this is a different root than the one we're rendering.
      nextRoot !== root // nextRoot 代表上一次任务重新要更新的root
    ) {
      const rootExpirationTime = root.expirationTime;
      requestWork(root, rootExpirationTime); // 请求工作
    }
    if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { // 防止无限更新循环
      // Reset this back to zero so subsequent updates don't throw.
      nestedUpdateCount = 0;
      invariant(
        false,
        'Maximum update depth exceeded. This can happen when a ' +
          'component repeatedly calls setState inside ' +
          'componentWillUpdate or componentDidUpdate. React limits ' +
          'the number of nested updates to prevent infinite loops.',
      );
    }
  }
  
  function deferredUpdates<A>(fn: () => A): A {
    const currentTime = requestCurrentTime();
    const previousExpirationContext = expirationContext;
    const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;
    expirationContext = computeAsyncExpiration(currentTime);
    isBatchingInteractiveUpdates = false;
    try {
      return fn();
    } finally {
      expirationContext = previousExpirationContext;
      isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
    }
  }
  
  function syncUpdates<A, B, C0, D, R>(
    fn: (A, B, C0, D) => R,
    a: A,
    b: B,
    c: C0,
    d: D,
  ): R {
    const previousExpirationContext = expirationContext;
    expirationContext = Sync;
    try {
      return fn(a, b, c, d);
    } finally {
      expirationContext = previousExpirationContext;
    }
  }
  
  // TODO: Everything below this is written as if it has been lifted to the
  // renderers. I'll do this in a follow-up.
  
  // Linked-list of roots
  let firstScheduledRoot: FiberRoot | null = null;
  let lastScheduledRoot: FiberRoot | null = null;
  
  let callbackExpirationTime: ExpirationTime = NoWork;
  let callbackID: *;
  let isRendering: boolean = false;
  let nextFlushedRoot: FiberRoot | null = null;
  let nextFlushedExpirationTime: ExpirationTime = NoWork;
  let lowestPriorityPendingInteractiveExpirationTime: ExpirationTime = NoWork;
  let deadlineDidExpire: boolean = false;
  let hasUnhandledError: boolean = false;
  let unhandledError: mixed | null = null;
  let deadline: Deadline | null = null;
  
  let isBatchingUpdates: boolean = false;
  let isUnbatchingUpdates: boolean = false;
  let isBatchingInteractiveUpdates: boolean = false;
  
  let completedBatches: Array<Batch> | null = null;
  
  let originalStartTimeMs: number = now();
  let currentRendererTime: ExpirationTime = msToExpirationTime(
    originalStartTimeMs,
  );
  let currentSchedulerTime: ExpirationTime = currentRendererTime;
  
  // Use these to prevent an infinite loop of nested updates
  const NESTED_UPDATE_LIMIT = 50;
  let nestedUpdateCount: number = 0;
  let lastCommittedRootDuringThisBatch: FiberRoot | null = null;
  
  const timeHeuristicForUnitOfWork = 1;
  
  function recomputeCurrentRendererTime() {
    const currentTimeMs = now() - originalStartTimeMs;
    currentRendererTime = msToExpirationTime(currentTimeMs); // msToExpirationTime 
  } 
  
  function scheduleCallbackWithExpirationTime(
    root: FiberRoot,
    expirationTime: ExpirationTime,
  ) {
    // callbackExpirationTime 对应上次调用这个方法 然后去调用react scheduler 去申请的callback
    // 这个callback也有 expirationTime  这里其实与用当前的进行比较 看哪个优先级高
    if (callbackExpirationTime !== NoWork) { // 之前的callback 在执行了
      // A callback is already scheduled. Check its expiration time (timeout).
      if (expirationTime > callbackExpirationTime) {
        // Existing callback has sufficient timeout. Exit.
        return;
      } else {
        if (callbackID !== null) {
          // Existing callback has insufficient timeout. Cancel and schedule a
          // new one.
          cancelDeferredCallback(callbackID); // 当前优先级高 则取消上一个callback
        }
      }
      // The request callback timer is already running. Don't start a new one.
    } else {
      startRequestCallbackTimer();
    }
  
    callbackExpirationTime = expirationTime;
    const currentMs = now() - originalStartTimeMs; // originalStartTimeMs react加载开始执行的时间
    const expirationTimeMs = expirationTimeToMs(expirationTime);
    const timeout = expirationTimeMs - currentMs; // 还剩多少时间将要过期
    callbackID = scheduleDeferredCallback(performAsyncWork, {timeout}); // 添加异步调度执行
  }
  
  // For every call to renderRoot, one of onFatal, onComplete, onSuspend, and
  // onYield is called upon exiting. We use these in lieu of returning a tuple.
  // I've also chosen not to inline them into renderRoot because these will
  // eventually be lifted into the renderer.
  function onFatal(root) {
    root.finishedWork = null;
  }
  
  function onComplete(
    root: FiberRoot,
    finishedWork: Fiber,
    expirationTime: ExpirationTime,
  ) {
    root.pendingCommitExpirationTime = expirationTime;
    root.finishedWork = finishedWork;
  }
  
  function onSuspend(
    root: FiberRoot,
    finishedWork: Fiber,
    suspendedExpirationTime: ExpirationTime,
    rootExpirationTime: ExpirationTime,
    msUntilTimeout: number,
  ): void {
    root.expirationTime = rootExpirationTime;
    // msUntilTimeout：nextRenderDidError的判断中传入的是 -1 因此下面两个判断都不符合
    // 也就是什么都没做 也就是在这个 renderRoot 中不提交更新
    if (msUntilTimeout === 0 && !shouldYield()) {
      // !shouldYield() 说明任务还没超时
      // Don't wait an additional tick. Commit the tree immediately.
      root.pendingCommitExpirationTime = suspendedExpirationTime;
      // 直接设置了 finishedWork 最终会直接调用 commitRoot
      root.finishedWork = finishedWork;
    } else if (msUntilTimeout > 0) {
      // Wait `msUntilTimeout` milliseconds before committing.
  
      // 在 react-dom 中 scheduleTimeout 是 window.setTimeOut 方法
      root.timeoutHandle = scheduleTimeout(
        // onTimeout 设置了 finishedWork 然后调flushRoot 强制调用的 commitRoot 强制更新
        onTimeout.bind(null, root, finishedWork, suspendedExpirationTime),
        msUntilTimeout,
      );
    }
  }
  
  function onYield(root) {
    root.finishedWork = null;
  }
  
  // 设置了 finishedWork 然后调flushRoot 强制调用的 commitRoot
  function onTimeout(root, finishedWork, suspendedExpirationTime) {
    // The root timed out. Commit it.
    root.pendingCommitExpirationTime = suspendedExpirationTime;
    root.finishedWork = finishedWork;
    // Read the current time before entering the commit phase. We can be
    // certain this won't cause tearing related to batching of event updates
    // because we're at the top of a timer event.
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;
    flushRoot(root, suspendedExpirationTime);
  }
  
  function onCommit(root, expirationTime) {
    root.expirationTime = expirationTime;
    root.finishedWork = null;
  }
  
  function requestCurrentTime() {
    // requestCurrentTime is called by the scheduler to compute an expiration
    // time.
    //
    // Expiration times are computed by adding to the current time (the start
    // time). However, if two updates are scheduled within the same event, we
    // should treat their start times as simultaneous, even if the actual clock
    // time has advanced between the first and second call.
  
    // In other words, because expiration times determine how updates are batched,
    // we want all updates of like priority that occur within the same event to
    // receive the same expiration time. Otherwise we get tearing.
    //
    // We keep track of two separate times: the current "renderer" time and the
    // current "scheduler" time. The renderer time can be updated whenever; it
    // only exists to minimize the calls performance.now.
    //
    // But the scheduler time can only be updated if there's no pending work, or
    // if we know for certain that we're not in the middle of an event.
  
    if (isRendering) {
      // We're already rendering. Return the most recently read time. 暂时就当做是js加载完成和现在时间的差
      return currentSchedulerTime;
    }
    // Check if there's pending work. 从调度队列中找到权限最高的root
    findHighestPriorityRoot();
    if (
      nextFlushedExpirationTime === NoWork ||
      nextFlushedExpirationTime === Never
      // nextFlushedExpirationTime === Never 表示产生更新的组件是个永远不会展示的组件
    ) {
      // If there's no pending work, or if the pending work is offscreen, we can
      // read the current time without risk of tearing.
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
      return currentSchedulerTime;
    }
    // There's already pending work. We might be in the middle of a browser
    // event. If we were to read the current time, it could cause multiple updates
    // within the same event to receive different expiration times, leading to
    // tearing. Return the last read time. During the next idle callback, the
    // time will be updated.
    return currentSchedulerTime;
  }
  
  // requestWork is called by the scheduler whenever a root receives an update.
  // It's up to the renderer to call renderRoot at some point in the future.
  function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
    addRootToSchedule(root, expirationTime); // expirationTime 记录任务过期时间
    if (isRendering) {
      // Prevent reentrancy. Remaining work will be scheduled at the end of
      // the currently rendering batch.
      return;
    }
  
    if (isBatchingUpdates) { // 批量处理的部分
      // Flush work at the end of the batch.
      if (isUnbatchingUpdates) {
        // ...unless we're inside unbatchedUpdates, in which case we should
        // flush it now.  nextFlushedRoot 表示下个要进行渲染的root 和 nextFlushedExpirationTime 它的过期时间
        nextFlushedRoot = root;
        nextFlushedExpirationTime = Sync;
        performWorkOnRoot(root, Sync, true);
      }
      return;
    }
  
    // TODO: Get rid of Sync and use current time?
    if (expirationTime === Sync) {
      performSyncWork(); // 同步调用 javascript代码， 一直执行完
    } else {
      scheduleCallbackWithExpirationTime(root, expirationTime); // 进入异步调度 使用requestidlecallback等
    }
  }
  // 将新的root加入要调度的 scheduledRot的 单向循环链表中
  function addRootToSchedule(root: FiberRoot, expirationTime: ExpirationTime) {
    // Add the root to the schedule.
    // Check if this root is already part of the schedule. 检查当前root是否已经在 调度中
    // nextScheduledRoot: FiberRoot 是用于关联root 链表的
    if (root.nextScheduledRoot === null) {
      // This root is not already scheduled. Add it.
  		root.expirationTime = expirationTime;
  		
  		// firstScheduledRoot lastScheduledRoot 等形成一个单向循环链表结构， 用来存取 react 应用中所有root:FiberRoot（假如多个root的话）的调度的关系
      if (lastScheduledRoot === null) { // 是否有任务在进行调度
        firstScheduledRoot = lastScheduledRoot = root;
        root.nextScheduledRoot = root;
      } else {
  			// 有的话把当前root 插入到调度的最后  就是一个单向循环链表的插入
        lastScheduledRoot.nextScheduledRoot = root;
        lastScheduledRoot = root;
        lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
      }
    } else {
      // This root is already scheduled, but its priority may have increased.
      const remainingExpirationTime = root.expirationTime; // 目前的expirationTime
      if (
        remainingExpirationTime === NoWork ||
        expirationTime < remainingExpirationTime
      ) { // 如果 新的优先级 高于 目前的优先级
        // Update the priority. priorit优先级 更新优先级  
        root.expirationTime = expirationTime;
      }
    }
  }
  
  function findHighestPriorityRoot() { // 寻找任务优先级最高的root节点，以及对应的expiration
    let highestPriorityWork = NoWork;
    let highestPriorityRoot = null;
    if (lastScheduledRoot !== null) { // 存在有任务的root节点
      let previousScheduledRoot = lastScheduledRoot;
      let root = firstScheduledRoot;
      while (root !== null) { // 存在有任务要执行的root节点
        const remainingExpirationTime = root.expirationTime;
        if (remainingExpirationTime === NoWork) { 
          // 此root不在要执行 从调度中移除 下面一系列操作就是为了将此root 从root的调度链表中移除
          // This root no longer has work. Remove it from the scheduler.
  
          // TODO: This check is redudant, but Flow is confused by the branch
          // below where we set lastScheduledRoot to null, even though we break
          // from the loop right after.
          invariant(
            previousScheduledRoot !== null && lastScheduledRoot !== null,
            'Should have a previous and last root. This error is likely ' +
              'caused by a bug in React. Please file an issue.',
          );
          if (root === root.nextScheduledRoot) { 
            // This is the only root in the list. 当前root是唯一的一个root 清空
            root.nextScheduledRoot = null;
            firstScheduledRoot = lastScheduledRoot = null;
            break;
          } else if (root === firstScheduledRoot) {
            // This is the first root in the list.
            const next = root.nextScheduledRoot;
            firstScheduledRoot = next;
            lastScheduledRoot.nextScheduledRoot = next;
            root.nextScheduledRoot = null;
          } else if (root === lastScheduledRoot) {
            // This is the last root in the list.
            lastScheduledRoot = previousScheduledRoot;
            lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
            root.nextScheduledRoot = null;
            break;
          } else {
            previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
            root.nextScheduledRoot = null;
          }
          root = previousScheduledRoot.nextScheduledRoot;
        } else {
          if (
            highestPriorityWork === NoWork ||
            remainingExpirationTime < highestPriorityWork
          ) {
            // Update the priority, if it's higher
            highestPriorityWork = remainingExpirationTime;
            highestPriorityRoot = root;
          }
          if (root === lastScheduledRoot) {
            break;
          }
          if (highestPriorityWork === Sync) {
            // Sync is highest priority by definition so
            // we can stop searching.
            break;
          }
          previousScheduledRoot = root;
          root = root.nextScheduledRoot;
        }
      }
    }
  
    nextFlushedRoot = highestPriorityRoot;
    nextFlushedExpirationTime = highestPriorityWork;
  }
  
  function performAsyncWork(dl) {
    if (dl.didTimeout) {
      // 过期任务要立马执行
      // The callback timed out. That means at least one update has expired.
      // Iterate through the root schedule. If they contain expired work, set
      // the next render expiration time to the current time. This has the effect
      // of flushing all expired work in a single batch, instead of flushing each
      // level one at a time.
      if (firstScheduledRoot !== null) {
        recomputeCurrentRendererTime();
        let root: FiberRoot = firstScheduledRoot;
        do {
          didExpireAtExpirationTime(root, currentRendererTime);
          // The root schedule is circular, so this is never null.
          root = (root.nextScheduledRoot: any);
        } while (root !== firstScheduledRoot);
      }
    }
    performWork(NoWork, dl);
  }
  
  function performSyncWork() {
    performWork(Sync, null);
  }
  
  function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
    deadline = dl;
  
    // Keep working on roots until there's no more work, or until we reach
    // the deadline.
    findHighestPriorityRoot();
  
    if (deadline !== null) {
      // 异步的执行
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
  
      if (enableUserTimingAPI) {
        const didExpire = nextFlushedExpirationTime < currentRendererTime;
        const timeout = expirationTimeToMs(nextFlushedExpirationTime);
        stopRequestCallbackTimer(didExpire, timeout);
      }
  
      while (
        nextFlushedRoot !== null &&
        nextFlushedExpirationTime !== NoWork &&
        (minExpirationTime === NoWork ||
          minExpirationTime >= nextFlushedExpirationTime) &&
        // performAsyncWork 来说 它的minExpirationTime > 1
        (!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
        // !deadlineDidExpire 代表时间片还有时间，
        // currentRendererTime >= nextFlushedExpirationTime 代表即将要执行的任务已经过期了 要马上执行
      ) {
        performWorkOnRoot(
          nextFlushedRoot,
          nextFlushedExpirationTime,
          currentRendererTime >= nextFlushedExpirationTime,
        );
        // 在执行完performWorkOnRoot 后再找一次优先级最高的 root 
        // 是根据 expirationTime 的大小判断的
        findHighestPriorityRoot();
        recomputeCurrentRendererTime();
        currentSchedulerTime = currentRendererTime;
      }
    } else {
      // 同步的情况 调用performSyncWork的时候
      while (
        nextFlushedRoot !== null &&
        nextFlushedExpirationTime !== NoWork &&
        (minExpirationTime === NoWork ||
          minExpirationTime >= nextFlushedExpirationTime)
      ) { 
        // performSyncWork 来说 它的minExpirationTime = 1 
        // 也即是说nextFlushedExpirationTime=1 才会执行 sync=1 是sync才会执行
        // 只会执行有同步任务的 root的更新
        performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);
        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 (deadline !== null) {
      callbackExpirationTime = NoWork;
      callbackID = null;
    }
    // If there's work left over, schedule a new callback.
    if (nextFlushedExpirationTime !== NoWork) {
      scheduleCallbackWithExpirationTime(
        ((nextFlushedRoot: any): FiberRoot),
        nextFlushedExpirationTime,
      );
    }
  
    // Clean-up.
    deadline = null;
    deadlineDidExpire = false;
  
    finishRendering();
  }
  
  // 强制更新
  function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {
    invariant(
      !isRendering,
      'work.commit(): Cannot commit while already rendering. This likely ' +
        'means you attempted to commit from inside a lifecycle method.',
    );
    // Perform work on root as if the given expiration time is the current time.
    // This has the effect of synchronously flushing all work up to and
    // including the given time.
    nextFlushedRoot = root;
    nextFlushedExpirationTime = expirationTime;
    performWorkOnRoot(root, expirationTime, true);
    // Flush any sync work that was scheduled by lifecycles
    performSyncWork();
  }
  
  function finishRendering() {
    nestedUpdateCount = 0;
    lastCommittedRootDuringThisBatch = null;
  
    if (completedBatches !== null) {
      const batches = completedBatches;
      completedBatches = null;
      for (let i = 0; i < batches.length; i++) {
        const batch = batches[i];
        try {
          batch._onComplete();
        } catch (error) {
          if (!hasUnhandledError) {
            hasUnhandledError = true;
            unhandledError = error;
          }
        }
      }
    }
  
    if (hasUnhandledError) {
      const error = unhandledError;
      unhandledError = null;
      hasUnhandledError = false;
      throw error;
    }
  }
  
  function performWorkOnRoot(
    root: FiberRoot,
    expirationTime: ExpirationTime,
    isExpired: 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. 检查是异步任务 或者是同步（过期）任务
    if (deadline === null || isExpired) {
      // 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).
  
      let finishedWork = root.finishedWork;
      // 判断是否是有完成了的任务
      if (finishedWork !== null) {
        // 完成了要及时commit  completeRoot 最终调用 commitRoot
        // 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.
        // 假如这个任务以前挂起了 就清除存在的超时，然后重新渲染
        // timeoutHandle 在任务被挂起的时候通过setTimeout设置的返回内容，用来下一次如果有新的任务挂起时清理还没触发的timeout
        const timeoutHandle = root.timeoutHandle;
        if (timeoutHandle !== noTimeout) {
          root.timeoutHandle = noTimeout;
          // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
          cancelTimeout(timeoutHandle);
        }
        const isYieldy = false; // 是否可以中断，  但这里的任务要么是sync任务要么已经过期 是不可以中断的
        renderRoot(root, isYieldy, isExpired);
        finishedWork = root.finishedWork;
        if (finishedWork !== null) {
          // We've completed the root. Commit it.
          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);
        }
        // 异步任务是可以中断的
        const isYieldy = true;
        renderRoot(root, isYieldy, isExpired);
        finishedWork = root.finishedWork;
        if (finishedWork !== null) {
          // We've completed the root. Check the deadline one more time
          // before committing.
          if (!shouldYield()) {
            // 如果时间片还有时间 记录一下
            // 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;
  }
  
  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;
    }
    commitRoot(root, finishedWork);
  }
  
  // When working on async work, the reconciler asks the renderer if it should
  // yield execution. For DOM, we implement this with requestIdleCallback.
  function shouldYield() { // 判断这一帧是否还有时间 deadline.timeRemaining() 表示剩余的时间
    if (deadlineDidExpire) {
      return true;
    }
    if (
      deadline === null ||
      deadline.timeRemaining() > timeHeuristicForUnitOfWork
    ) {
      // Disregard deadline.didTimeout. Only expired work should be flushed
      // during a timeout. This path is only hit for non-expired work.
      return false;
    }
    deadlineDidExpire = true;
    return true;
  }
  
  function onUncaughtError(error: mixed) {
    invariant(
      nextFlushedRoot !== null,
      'Should be working on a root. This error is likely caused by a bug in ' +
        'React. Please file an issue.',
    );
    // Unschedule this root so we don't work on it again until there's
    // another update.
    // 直接将剩下的任务都不执行了
    nextFlushedRoot.expirationTime = NoWork;
    if (!hasUnhandledError) {
      hasUnhandledError = true;
      unhandledError = error;
    }
  }
  
  // TODO: Batching should be implemented at the renderer level, not inside
  // the reconciler.
  function batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
    const previousIsBatchingUpdates = isBatchingUpdates;
    isBatchingUpdates = true;
    try {
      return fn(a);
    } finally {
      isBatchingUpdates = previousIsBatchingUpdates;
      if (!isBatchingUpdates && !isRendering) {
        performSyncWork();
      }
    }
  }
  
  // TODO: Batching should be implemented at the renderer level, not inside
  // the reconciler.
  function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
    if (isBatchingUpdates && !isUnbatchingUpdates) {
      isUnbatchingUpdates = true;
      try {
        return fn(a);
      } finally {
        isUnbatchingUpdates = false;
      }
    }
    return fn(a);
  }
  
  // TODO: Batching should be implemented at the renderer level, not within
  // the reconciler.
  function flushSync<A, R>(fn: (a: A) => R, a: A): R {
    invariant(
      !isRendering,
      'flushSync was called from inside a lifecycle method. It cannot be ' +
        'called when React is already rendering.',
    );
    const previousIsBatchingUpdates = isBatchingUpdates;
    isBatchingUpdates = true;
    try {
      return syncUpdates(fn, a);
    } finally {
      isBatchingUpdates = previousIsBatchingUpdates;
      performSyncWork();
    }
  }
  
  function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
    if (isBatchingInteractiveUpdates) {
      return fn(a, b);
    }
    // If there are any pending interactive updates, synchronously flush them.
    // This needs to happen before we read any handlers, because the effect of
    // the previous event may influence which handlers are called during
    // this event.
    if (
      !isBatchingUpdates &&
      !isRendering &&
      lowestPriorityPendingInteractiveExpirationTime !== NoWork
    ) {
      // Synchronously flush pending interactive updates.
      performWork(lowestPriorityPendingInteractiveExpirationTime, null);
      lowestPriorityPendingInteractiveExpirationTime = NoWork;
    }
    const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;
    const previousIsBatchingUpdates = isBatchingUpdates;
  
    // 设置了这个两个全局变量 不会 立马就去执行 performWork的 操作
    isBatchingInteractiveUpdates = true;
    isBatchingUpdates = true;
    try {
      return fn(a, b);
    } finally {
      isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
      isBatchingUpdates = previousIsBatchingUpdates;
      if (!isBatchingUpdates && !isRendering) {
        performSyncWork();
      }
    }
  }
  
  function flushInteractiveUpdates() {
    if (
      !isRendering &&
      lowestPriorityPendingInteractiveExpirationTime !== NoWork
    ) {
      // Synchronously flush pending interactive updates.
      performWork(lowestPriorityPendingInteractiveExpirationTime, null);
      lowestPriorityPendingInteractiveExpirationTime = NoWork;
    }
  }
  
  function flushControlled(fn: () => mixed): void {
    const previousIsBatchingUpdates = isBatchingUpdates;
    isBatchingUpdates = true;
    try {
      syncUpdates(fn);
    } finally {
      isBatchingUpdates = previousIsBatchingUpdates;
      if (!isBatchingUpdates && !isRendering) {
        performSyncWork();
      }
    }
  }
  
  export {
    requestCurrentTime,
    computeExpirationForFiber,
    captureCommitPhaseError,
    onUncaughtError,
    renderDidSuspend,
    renderDidError,
    retrySuspendedRoot,
    markLegacyErrorBoundaryAsFailed,
    isAlreadyFailedLegacyErrorBoundary,
    scheduleWork,
    requestWork,
    flushRoot,
    batchedUpdates,
    unbatchedUpdates,
    flushSync,
    flushControlled,
    deferredUpdates,
    syncUpdates,
    interactiveUpdates,
    flushInteractiveUpdates,
    computeUniqueAsyncExpiration,
  };
  