• react源码中的协调与调度


    requestEventTime

    其实在React执行过程中,会有数不清的任务要去执行,但是他们会有一个优先级的判定,假如两个事件的优先级一样,那么React是怎么去判定他们两谁先执行呢?

    // packages/react-reconciler/src/ReactFiberWorkLoop.old.js
    export function requestEventTime() {
       
      if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
       
        // We're inside React, so it's fine to read the actual time.
        // react事件正在执行
        // executionContext
        // RenderContext 正在计算
        // CommitContext 正在提交
        // export const NoContext = /*             */ 0b0000000;
        // const BatchedContext = /*               */ 0b0000001;
        // const EventContext = /*                 */ 0b0000010;
        // const DiscreteEventContext = /*         */ 0b0000100;
        // const LegacyUnbatchedContext = /*       */ 0b0001000;
        // const RenderContext = /*                */ 0b0010000;
        // const CommitContext = /*                */ 0b0100000;
        // export const RetryAfterError = /*       */ 0b1000000;
        return now();
      }
      // 没有在react事件执行 NoTimestamp === -1
      if (currentEventTime !== NoTimestamp) {
        
        // 浏览器事件正在执行,返回上次的 currentEventTime
        return currentEventTime;
      }
      // 重新计算currentEventTime,当执行被中断后
      currentEventTime = now();
      return currentEventTime;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • RenderContextCommitContext表示正在计算更新和正在提交更新,返回now()
    • 如果是浏览器事件正在执行中,返回上一次的currentEventTime
    • 如果终止或者中断react任务执行的时候,则重新获取执行时间now()。
    • 获取的时间越小,则执行的优先级越高

    now()并不是单纯的new Date(),而是判定两次更新任务的时间是否小于10ms,来决定是否复用上一次的更新时间Scheduler_now的。

    export const now = initialTimeMs < 10000 ? Scheduler_now : () => Scheduler_now() - initialTimeMs;
    
    
    • 1
    • 2

    其实各位猜想一下,对于10ms级别的任务间隙时间,几乎是可以忽略不计的,那么这里就可以视为同样的任务,不需要有很大的性能开销,有利于批量更新

    requestUpdateLane

    requestEventTime位每一个需要执行的任务打上了触发更新时间标签,那么任务的优先级还需要进一步的确立,requestUpdateLane就是用来获取每一个任务执行的优先级的。

    // packages/react-reconciler/src/ReactFiberWorkLoop.old.js
    export function requestUpdateLane(fiber: Fiber): Lane {
       
      // Special cases
      const mode = fiber.mode;
      if ((mode & BlockingMode) === NoMode) {
       
        return (SyncLane: Lane);
      } else if ((mode & ConcurrentMode) === NoMode) {
       
        return getCurrentPriorityLevel() === ImmediateSchedulerPriority
          ? (SyncLane: Lane)
          : (SyncBatchedLane: Lane);
      } else if (
        !deferRenderPhaseUpdateToNextBatch &&
        (executionContext & RenderContext) !== NoContext &&
        workInProgressRootRenderLanes !== NoLanes
      ) {
       
        // This is a render phase update. These are not officially supported. The
        // old behavior is to give this the same "thread" (expiration time) as
        // whatever is currently rendering. So if you call `setState` on a component
        // that happens later in the same render, it will flush. Ideally, we want to
        // remove the special case and treat them as if they came from an
        // interleaved event. Regardless, this pattern is not officially supported.
        // This behavior is only a fallback. The flag only exists until we can roll
        // out the setState warning, since existing code might accidentally rely on
        // the current behavior.
        return pickArbitraryLane(workInProgressRootRenderLanes);
      }
    
      // The algorithm for assigning an update to a lane should be stable for all
      // updates at the same priority within the same event. To do this, the inputs
      // to the algorithm must be the same. For example, we use the `renderLanes`
      // to avoid choosing a lane that is already in the middle of rendering.
      //
      // However, the "included" lanes could be mutated in between updates in the
      // same event, like if you perform an update inside `flushSync`. Or any other
      // code path that might call `prepareFreshStack`.
      //
      // The trick we use is to cache the first of each of these inputs within an
      // event. Then reset the cached values once we can be sure the event is over.
      // Our heuristic for that is whenever we enter a concurrent work loop.
      //
      // We'll do the same for `currentEventPendingLanes` below.
      if (currentEventWipLanes === NoLanes) {
       
        currentEventWipLanes = workInProgressRootIncludedLanes;
      }
    
      const isTransition = requestCurrentTransition() !== NoTransition;
      if (isTransition) {
       
        if (currentEventPendingLanes !== NoLanes) {
       
          currentEventPendingLanes =
            mostRecentlyUpdatedRoot !== null
              ? mostRecentlyUpdatedRoot.pendingLanes
              : NoLanes;
        }
        return findTransitionLane(currentEventWipLanes, currentEventPendingLanes);
      }
    
      // TODO: Remove this dependency on the Scheduler priority.
      // To do that, we're replacing it with an update lane priority.
    
      // 获取执行任务的优先级,便于调度
      const schedulerPriority = getCurrentPriorityLevel();
    
      // The old behavior was using the priority level of the Scheduler.
      // This couples React to the Scheduler internals, so we're replacing it
      // with the currentUpdateLanePriority above. As an example of how this
      // could be problematic, if we're not inside `Scheduler.runWithPriority`,
      // then we'll get the priority of the current running Scheduler task,
      // which is probably not what we want.
      let lane;
      if (
        // TODO: Temporary. We're removing the concept of discrete updates.
        (executionContext & DiscreteEventContext) !== NoContext &&
    
        // 用户block的类型事件
        schedulerPriority === UserBlockingSchedulerPriority
      ) {
       
        // 通过findUpdateLane函数重新计算lane
        lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes);
      } else {
       
        // 根据优先级计算法则计算lane
        const schedulerLanePriority = schedulerPriorityToLanePriority(
          schedulerPriority,
        );
    
        if (decoupleUpdatePriorityFromScheduler) {
       
          // In the new strategy, we will track the current update lane priority
          // inside React and use that priority to select a lane for this update.
          // For now, we're just logging when they're different so we can assess.
          const currentUpdateLanePriority = getCurrentUpdateLanePriority();
    
          if (
            schedulerLanePriority !== currentUpdateLanePriority &&
            currentUpdateLanePriority !== NoLanePriority
          ) {
       
            if (__DEV__) {
       
              console.error(
                'Expected current scheduler lane priority %s to match current update lane priority %s',
                schedulerLanePriority,
                currentUpdateLanePriority,
              );
            }
          }
        }
        // 根据计算得到的 schedulerLanePriority,计算更新的优先级 lane
        lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
      }
    
      return lane;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 通过getCurrentPriorityLevel获得所有执行任务的调度优先级schedulerPriority
    • 通过findUpdateLane计算lane,作为更新中的优先级。

    findUpdateLane

    export function findUpdateLane(
      lanePriority: LanePriority,  wipLanes: Lanes,
    ): Lane {
       
      switch (lanePriority) {
       
        case NoLanePriority:
          break;
        case SyncLanePriority:
          return SyncLane;
        case SyncBatchedLanePriority:
          return SyncBatchedLane;
        case InputDiscreteLanePriority: {
       
          const lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);
          if (lane === NoLane) {
       
            // Shift to the next priority level
            return findUpdateLane(InputContinuousLanePriority, wipLanes);
          }
          return lane;
        }
        case InputContinuousLanePriority: {
       
          const lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);
          if (lane === NoLane) {
       
            // Shift to the next priority level
            return findUpdateLane(DefaultLanePriority, wipLanes);
          }
          return lane;
        }
        case DefaultLanePriority: {
       
          let lane = pickArbitraryLane(DefaultLanes & ~wipLanes);
          if (lane === NoLane) {
       
            // If all the default lanes are already being worked on, look for a
            // lane in the transition range.
            lane = pickArbitraryLane(TransitionLanes & ~wipLanes);
            if (lane === NoLane) {
       
              // All the transition lanes are taken, too. This should be very
              // rare, but 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
  • 相关阅读:
    软件设计模式系列之十六——命令模式
    UMA 2 - Unity Multipurpose Avatar☀️八.UMA内置实用Recipes插件
    智能语音外呼OKCC呼叫中心的各项指标KPI
    导入Maven项目遇到的一些问题及解决
    统计学习方法学习笔记-07-支持向量机03
    webpack的使用
    Java老人护理上门服务类型系统小程序APP源码
    14. Redisson 分布式锁
    Redis缓存穿透、缓存雪崩和缓存击穿
    2022前端面试题总结
  • 原文地址:https://blog.csdn.net/weixin_59558923/article/details/127401624