初窥React-8 (scheduleUpdateOnFiber方法-1)

 

// Register pending interactions on the root to avoid losing traced interaction data.
 schedulePendingInteractions(root, lane);

  scheduleInteractions 会利用FiberRoot的 pendingInteractionMap 属性和不同的 expirationTime,获取每次schedule所需的update任务的集合,记录它们的数量,并检测这些任务是否会出错。

function scheduleInteractions(root, lane, interactions) {

    if (interactions.size > 0) {
      var pendingInteractionMap = root.pendingInteractionMap;
      var pendingInteractions = pendingInteractionMap.get(lane);

      if (pendingInteractions != null) {
        interactions.forEach(function (interaction) {
          if (!pendingInteractions.has(interaction)) {
            // Update the pending async work count for previously unscheduled interaction.
            interaction.__count++;
          }

          pendingInteractions.add(interaction);
        });
      } else {
        pendingInteractionMap.set(lane, new Set(interactions)); // Update the pending async work count for the current interactions.

        interactions.forEach(function (interaction) {
          interaction.__count++;
        });
      }

      var subscriber = __subscriberRef.current;

      if (subscriber !== null) {
        var threadID = computeThreadID(root, lane);
        subscriber.onWorkScheduled(interactions, threadID);
      }
    }
  }

 

 

// root inside of batchedUpdates should be synchronous, but layout updates
// should be deferred until the end of the batch.
performSyncWorkOnRoot(root);

 重要方法,SyncLane === lane的时候,其中ensureRootIsScheduled中也调用了performSyncWorkOnRoot.

function performSyncWorkOnRoot(root) {
    if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {
      {
        throw Error( "Should not already be working." );
      }
    }

    flushPassiveEffects();
    var lanes;
    var exitStatus;
  
    if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) {
    //如果workInProgressLanes中有expiredLanes,直接render
// There's a partial tree, and at least one of its lanes has expired. Finish // rendering it before rendering the rest of the expired work. lanes = workInProgressRootRenderLanes; exitStatus = renderRootSync(root, lanes); if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {
     // 本次包括了所有正在渲染阶段的lane
// The render included lanes that were updated during the render phase. // For example, when unhiding a hidden tree, we include all the lanes // that were previously skipped when the tree was hidden. That set of // lanes is a superset of the lanes we started rendering with. // // Note that this only happens when part of the tree is rendered // concurrently. If the whole tree is rendered synchronously, then there // are no interleaved events. lanes = getNextLanes(root, lanes); exitStatus = renderRootSync(root, lanes); } } else { lanes = getNextLanes(root, NoLanes); exitStatus = renderRootSync(root, lanes); }    if (root.tag !== LegacyRoot && exitStatus === RootErrored) { executionContext |= RetryAfterError; // If an error occurred during hydration, // discard server response and fall back to client side render. if (root.hydrate) { root.hydrate = false; clearContainer(root.containerInfo); } // If something threw an error, try rendering one more time. We'll render // synchronously to block concurrent data mutations, and we'll includes // all pending updates are included. If it still fails after the second // attempt, we'll give up and commit the resulting tree. lanes = getLanesToRetrySynchronouslyOnError(root); if (lanes !== NoLanes) { exitStatus = renderRootSync(root, lanes); } } if (exitStatus === RootFatalErrored) { var fatalError = workInProgressRootFatalError; prepareFreshStack(root, NoLanes); markRootSuspended$1(root, lanes); ensureRootIsScheduled(root, now()); throw fatalError; }
   //由于这个是sync的,所以不存在插队直接commit
// We now have a consistent tree. Because this is a sync render, we // will commit it even if something suspended. var finishedWork = root.current.alternate; root.finishedWork = finishedWork; root.finishedLanes = lanes; commitRoot(root); // Before exiting, make sure there's a callback scheduled for the next // pending level.   
    //调度决策的逻辑在ensureRootIsScheduled 函数中, 任务优先级在即将调度的时候去计算,代码在ensureRootIsScheduled函数中
    ensureRootIsScheduled(root, now());
    return null;
  }

 

   通过调用getNextLanes去计算在本次更新中应该处理的这批lanes(nextLanes),getNextLanes会调用getHighestPriorityLanes去计算任务优先级。任务优先级计算的原理是这样:更新优先级(update的lane),它会被并入root.pendingLanes,root.pendingLanes经过getNextLanes处理后,挑出那些应该处理的lanes,传入getHighestPriorityLanes,根据nextLanes找出这些lanes的优先级作为任务优先级。

  function getNextLanes(root, wipLanes) {
    // Early bailout if there's no pending work left.
    var pendingLanes = root.pendingLanes;

    if (pendingLanes === NoLanes) {
      return_highestLanePriority = NoLanePriority;
      return NoLanes;
    }

    var nextLanes = NoLanes;
    var nextLanePriority = NoLanePriority;
    var expiredLanes = root.expiredLanes;
    var suspendedLanes = root.suspendedLanes;
    var pingedLanes = root.pingedLanes; // Check if any work has expired.

    if (expiredLanes !== NoLanes) {
      nextLanes = expiredLanes;
      nextLanePriority = return_highestLanePriority = SyncLanePriority;
    } else {
      // Do not work on any idle work until all the non-idle work has finished,
      // even if the work is suspended.
      var nonIdlePendingLanes = pendingLanes & NonIdleLanes;

      if (nonIdlePendingLanes !== NoLanes) {
        var nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;

        if (nonIdleUnblockedLanes !== NoLanes) {
          nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
          nextLanePriority = return_highestLanePriority;
        } else {
          var nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;

          if (nonIdlePingedLanes !== NoLanes) {
            nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
            nextLanePriority = return_highestLanePriority;
          }
        }
      } else {
        // The only remaining work is Idle.
        var unblockedLanes = pendingLanes & ~suspendedLanes;

        if (unblockedLanes !== NoLanes) {
          nextLanes = getHighestPriorityLanes(unblockedLanes);
          nextLanePriority = return_highestLanePriority;
        } else {
          if (pingedLanes !== NoLanes) {
            nextLanes = getHighestPriorityLanes(pingedLanes);
            nextLanePriority = return_highestLanePriority;
          }
        } 
      }
    }

    if (nextLanes === NoLanes) {
      // This should only be reachable if we're suspended
      // TODO: Consider warning in this path if a fallback timer is not scheduled.
      return NoLanes;
    } // If there are higher priority lanes, we'll include them even if they
    // are suspended.


    nextLanes = pendingLanes & getEqualOrHigherPriorityLanes(nextLanes); // If we're already in the middle of a render, switching lanes will interrupt
    // it and we'll lose our progress. We should only do this if the new lanes are
    // higher priority.

    if (wipLanes !== NoLanes && wipLanes !== nextLanes && // If we already suspended with a delay, then interrupting is fine. Don't
    // bother waiting until the root is complete.
    (wipLanes & suspendedLanes) === NoLanes) {
      getHighestPriorityLanes(wipLanes);
      var wipLanePriority = return_highestLanePriority;

      if (nextLanePriority <= wipLanePriority) {
        return wipLanes;
      } else {
        return_highestLanePriority = nextLanePriority;
      }
    } // Check for entangled lanes and add them to the batch.
    //
    // A lane is said to be entangled with another when it's not allowed to render
    // in a batch that does not also include the other lane. Typically we do this
    // when multiple updates have the same source, and we only want to respond to
    // the most recent event from that source.
    //
    // Note that we apply entanglements *after* checking for partial work above.
    // This means that if a lane is entangled during an interleaved event while
    // it's already rendering, we won't interrupt it. This is intentional, since
    // entanglement is usually "best effort": we'll try our best to render the
    // lanes in the same batch, but it's not worth throwing out partially
    // completed work in order to do it.
    //
    // For those exceptions where entanglement is semantically important, like
    // useMutableSource, we should ensure that there is no partial work at the
    // time we apply the entanglement.


    var entangledLanes = root.entangledLanes;

    if (entangledLanes !== NoLanes) {
      var entanglements = root.entanglements;
      var lanes = nextLanes & entangledLanes;

      while (lanes > 0) {
        var index = pickArbitraryLaneIndex(lanes);
        var lane = 1 << index;
        nextLanes |= entanglements[index];
        lanes &= ~lane;
      }
    }

    return nextLanes;
  }

 

 

 

 

 

 

 

 

 

 

 

 

上一节  下一节

posted @ 2021-07-11 14:26  AllenLiu0927  阅读(330)  评论(0)    收藏  举报