初窥React-9 (scheduleUpdateOnFiber方法-2)

        ensureRootIsScheduled(root, eventTime)中跟scheduler相关的主要方法可以看scheduleSyncCallback()或者scheduleCallback(),参数是performSyncWorkOnRoot Fn,继续dive,可以看到其实它们都是调用了Scheduler_scheduleCallback,分别是Scheduler_scheduleCallback(Scheduler_ImmediatePriority, flushSyncCallbackQueueImpl)和Scheduler_scheduleCallback(priorityLevel, callback, options),这里的核心方法就是Scheduler_scheduleCallback,首先判断是否是delay的任务,如果是,就执行requestHostTimeout,如果不是就执行requestHostCallback,当然中间还处理了taskQuene 和timerQuene。这两个队列顾名思义,taskQuene是任务队列,timerQuene就是为了后续让,被delay的任务能在delay ms 之后执行的一个依据 (通过 advanceTimers 方法 advanceTimers讲队列按时常排序);

  requestHostTimeout实际最后经过advanceTimers对规则之后,也是调用了requestHostCallback;而requestHostCallback(flushWork)则把flushWork(其中主要方法是workLoop,workLoop实际决定了是否任务完成或者让行)作为参数

 

//调度决策的逻辑在ensureRootIsScheduled 函数中, 任务优先级在即将调度的时候去计算,代码在ensureRootIsScheduled函数中:
  function ensureRootIsScheduled(root, currentTime) {
    var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as
    // expired so we know to work on those next.

    markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.
    //通过调用getNextLanes去计算在本次更新中应该处理的这批lanes(nextLanes)
    //getNextLanes会调用getHighestPriorityLanes去计算任务优先级。任务优先级计算的原理是这样:更新优先级(update的lane),
    //它会被并入root.pendingLanes,root.pendingLanes经过getNextLanes处理后,挑出那些应该处理的lanes,传入getHighestPriorityLanes,
    //根据nextLanes找出这些lanes的优先级作为任务优先级。
    var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); // This returns the priority level computed during the `getNextLanes` call.

    var newCallbackPriority = returnNextLanesPriority();

    if (nextLanes === NoLanes) {
      // Special case: There's nothing to work on.
      if (existingCallbackNode !== null) {
        cancelCallback(existingCallbackNode);
        root.callbackNode = null;
        root.callbackPriority = NoLanePriority;
      }

      return;
    } // Check if there's an existing task. We may be able to reuse it.


    if (existingCallbackNode !== null) {
      var existingCallbackPriority = root.callbackPriority;

      if (existingCallbackPriority === newCallbackPriority) {
        // The priority hasn't changed. We can reuse the existing task. Exit.
        return;
      } // The priority changed. Cancel the existing callback. We'll schedule a new
      // one below.


      cancelCallback(existingCallbackNode);
    } // Schedule a new callback.


    var newCallbackNode;

    if (newCallbackPriority === SyncLanePriority) {
      // Special case: Sync React callbacks are scheduled on a special
      // internal queue
      newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root)); //核心的 scheduler
    } else if (newCallbackPriority === SyncBatchedLanePriority) {
      newCallbackNode = scheduleCallback(ImmediatePriority$1, performSyncWorkOnRoot.bind(null, root));
    } else {
      var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
      newCallbackNode = scheduleCallback(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));
    }

    root.callbackPriority = newCallbackPriority;
    root.callbackNode = newCallbackNode;
  }

真正核心的scheduler:

  function unstable_scheduleCallback(priorityLevel, callback, options) {
    var currentTime = getCurrentTime();
    var startTime;

    if (typeof options === 'object' && options !== null) {
      var delay = options.delay;

      if (typeof delay === 'number' && delay > 0) {
        startTime = currentTime + delay;
      } else {
        startTime = currentTime;
      }
    } else {
      startTime = currentTime;
    }

    var timeout;

    switch (priorityLevel) {
      case ImmediatePriority:
        timeout = IMMEDIATE_PRIORITY_TIMEOUT;
        break;

      case UserBlockingPriority:
        timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
        break;

      case IdlePriority:
        timeout = IDLE_PRIORITY_TIMEOUT;
        break;

      case LowPriority:
        timeout = LOW_PRIORITY_TIMEOUT;
        break;

      case NormalPriority:
      default:
        timeout = NORMAL_PRIORITY_TIMEOUT;
        break;
    }
    //计算expirationTime主要根据之前的timeout和startTime
    var expirationTime = startTime + timeout;
    var newTask = {
      id: taskIdCounter++,
      callback: callback,
      priorityLevel: priorityLevel,
      startTime: startTime,
      expirationTime: expirationTime,
      sortIndex: -1
    };

    {
      newTask.isQueued = false;
    }
    //开始时间比现在的还晚,那么说明延误了,就是delay的
    //delay->requestHostTimeout
    //not delay->requestHostCallback
    //taskQuene是任务队列,timerQuene就是为了后续让,
    // 被delay的任务能在delay ms 之后执行的一个依据 (通过 advanceTimers 方法 advanceTimers讲队列按时常排序)
    if (startTime > currentTime) {
      // This is a delayed task.
      newTask.sortIndex = startTime;
      push(timerQueue, newTask);

      if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
        // All tasks are delayed, and this is the task with the earliest delay.
        if (isHostTimeoutScheduled) {
          // Cancel an existing timeout.
          cancelHostTimeout();
        } else {
          isHostTimeoutScheduled = true;
        } // Schedule a timeout.
        

        requestHostTimeout(handleTimeout, startTime - currentTime);
      }
    } else {
      newTask.sortIndex = expirationTime;
      push(taskQueue, newTask);

      {
        markTaskStart(newTask, currentTime);
        newTask.isQueued = true;
      } // Schedule a host callback, if needed. If we're already performing work,
      // wait until the next time we yield.


      if (!isHostCallbackScheduled && !isPerformingWork) {
        isHostCallbackScheduled = true;
        requestHostCallback(flushWork);
      }
    }

    return newTask;
  }

 

requestHostTimeout:
requestHostTimeout = function (cb, ms) {
   _timeoutID = setTimeout(cb, ms);
};

//cb: 其中,requestHostCallback传入的是flushWork,
function handleTimeout(currentTime) {
    isHostTimeoutScheduled = false;
    advanceTimers(currentTime);

    if (!isHostCallbackScheduled) {
      if (peek(taskQueue) !== null) {
        isHostCallbackScheduled = true;
        requestHostCallback(flushWork); //-------------------------------这里也调用requestHostCalback
      } else {
        var firstTimer = peek(timerQueue);

        if (firstTimer !== null) {
          requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
        }
      }
    }
  }
 
function flushWork(hasTimeRemaining, initialTime) {
    {
      markSchedulerUnsuspended(initialTime);
    } // We'll need a host callback the next time work is scheduled.


    isHostCallbackScheduled = false;

    if (isHostTimeoutScheduled) { //如果在执行 delay的任务,cancel掉
      // We scheduled a timeout but it's no longer needed. Cancel it.
      isHostTimeoutScheduled = false;
      cancelHostTimeout();
    }

    isPerformingWork = true;
    var previousPriorityLevel = currentPriorityLevel;

    try {
      if (enableProfiling) {
        try {
          return workLoop(hasTimeRemaining, initialTime); //---------------------------------------下面会讲
        } catch (error) {
          if (currentTask !== null) {
            var currentTime = getCurrentTime();
            markTaskErrored(currentTask, currentTime);
            currentTask.isQueued = false;
          }

          throw error;
        }
      } else {
        // No catch in prod code path.
        return workLoop(hasTimeRemaining, initialTime);
      }
    } finally {
      currentTask = null;
      currentPriorityLevel = previousPriorityLevel;
      isPerformingWork = false;

      {
        var _currentTime = getCurrentTime();

        markSchedulerSuspended(_currentTime);
      }
    }
  }
 

 

 

requestHostCallback:
 
//这里的cb是flushWork 
requestHostCallback = function (cb) { if (_callback !== null) { // Protect against re-entrancy. setTimeout(requestHostCallback, 0, cb); } else { _callback = cb; setTimeout(_flushCallback, 0); } };
      
  var _flushCallback = function () {
      if (_callback !== null) {
        try {
          var currentTime = getCurrentTime();
          var hasRemainingTime = true;

          _callback(hasRemainingTime, currentTime);

          _callback = null;
        } catch (e) {
          setTimeout(_flushCallback, 0);
          throw e;
        }
      }
    };
//flush上面讲了,里面主要是workLoop看下面的解释

 

 workLoop

function workLoop(hasTimeRemaining, initialTime) {
    var currentTime = initialTime;
    advanceTimers(currentTime);
    currentTask = peek(taskQueue);

    while (currentTask !== null && !(enableSchedulerDebugging )) {
      if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost())) {
        // This currentTask hasn't expired, and we've reached the deadline.
        break;
      }

      var callback = currentTask.callback;

      if (typeof callback === 'function') {
        currentTask.callback = null;
        currentPriorityLevel = currentTask.priorityLevel;
        var didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
        markTaskRun(currentTask, currentTime);
        var continuationCallback = callback(didUserCallbackTimeout);
        currentTime = getCurrentTime();

        if (typeof continuationCallback === 'function') {
          currentTask.callback = continuationCallback;             //这里的callback是performSyncOnWorkRoot
          markTaskYield(currentTask, currentTime);
        } else {
          {
            markTaskCompleted(currentTask, currentTime);
            currentTask.isQueued = false;
          }

          if (currentTask === peek(taskQueue)) {
            pop(taskQueue);
          }
        }

        advanceTimers(currentTime);
      } else {
        pop(taskQueue);
      }

      currentTask = peek(taskQueue);
    } // Return whether there's additional work


    if (currentTask !== null) {
      return true;
    } else {
      var firstTimer = peek(timerQueue);

      if (firstTimer !== null) {
        requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
      }

      return false;
    }
  }

 

 

 

  //在调度优先级的过程中,会调用markStarvedLanesAsExpired遍历pendingLanes(未执行的任务包含的lane),
  //如果没过期时间就计算一个过期时间,如果过期了就加入root.expiredLanes中,
  //然后在下次调用getNextLane函数的时候会优先返回expiredLanes
  function markStarvedLanesAsExpired(root, currentTime) {
    // TODO: This gets called every time we yield. We can optimize by storing
    // the earliest expiration time on the root. Then use that to quickly bail out
    // of this function.
    var pendingLanes = root.pendingLanes;
    var suspendedLanes = root.suspendedLanes;
    var pingedLanes = root.pingedLanes;
    var expirationTimes = root.expirationTimes; // Iterate through the pending lanes and check if we've reached their
    // expiration time. If so, we'll assume the update is being starved and mark
    // it as expired to force it to finish.

    var lanes = pendingLanes;

    while (lanes > 0) {
      //从pendingLane随便来一条
      var index = pickArbitraryLaneIndex(lanes);
      //lane向左移动一位
      var lane = 1 << index;
      //获得当前的expirationTime
      var expirationTime = expirationTimes[index];

      //如果是noTimestamp
      if (expirationTime === NoTimestamp) {
        // Found a pending lane with no expiration time. If it's not suspended, or
        // if it's pinged, assume it's CPU-bound. Compute a new expiration time
        // using the current time.
    
        //根据当前时间计算过期时间
        if ((lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes) {
          // Assumes timestamps are monotonically increasing.
          expirationTimes[index] = computeExpirationTime(lane, currentTime);
        }

        //如果过期了,加入到expirationTimes
      } else if (expirationTime <= currentTime) {
        // This lane expired
        root.expiredLanes |= lane;
      }
    
      lanes &= ~lane;
    }
  } 

 

//根据当前时间计算expiration time 位于markStarvedLanesAsExpired 方法中.
function computeExpirationTime(lane, currentTime) {
    // TODO: Expiration heuristic is constant per lane, so could use a map.
    getHighestPriorityLanes(lane);
    var priority = return_highestLanePriority;

    if (priority >= InputContinuousLanePriority) {
      // User interactions should expire slightly more quickly.
      //
      // NOTE: This is set to the corresponding constant as in Scheduler.js. When
      // we made it larger, a product metric in www regressed, suggesting there's
      // a user interaction that's being starved by a series of synchronous
      // updates. If that theory is correct, the proper solution is to fix the
      // starvation. However, this scenario supports the idea that expiration
      // times are an important safeguard when starvation does happen.
      //
      // Also note that, in the case of user input specifically, this will soon no
      // longer be an issue because we plan to make user input synchronous by
      // default (until you enter `startTransition`, of course.)
      //
      // If weren't planning to make these updates synchronous soon anyway, I
      // would probably make this number a configurable parameter.
      return currentTime + 250;
    } else if (priority >= TransitionPriority) {
      return currentTime + 5000;
    } else {
      // Anything idle priority or lower should never expire.
      return NoTimestamp;
    }
  }

 

 

 下面这俩方法位于scheduleUpdateOnFiber中..目的应该是清空状态

function resetRenderTimer() {
    workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS;
  }

 

//如果即时节点存在,则中断当前任务,从链表移除task
function
flushSyncCallbackQueue() { if (immediateQueueCallbackNode !== null) { var node = immediateQueueCallbackNode; immediateQueueCallbackNode = null; Scheduler_cancelCallback(node); }    //更新同步队列 flushSyncCallbackQueueImpl(); } function flushSyncCallbackQueueImpl() { if (!isFlushingSyncQueue && syncQueue !== null) { // Prevent re-entrancy. 防止重入 isFlushingSyncQueue = true; var i = 0; { try { var _isSync2 = true; var _queue = syncQueue;
//遍历同步队列,并更新刷新状态isSync=true runWithPriority$
1(ImmediatePriority$1, function () { for (; i < _queue.length; i++) { var callback = _queue[i]; do { callback = callback(_isSync2); } while (callback !== null); } }); syncQueue = null; } catch (error) { // If something throws, leave the remaining callbacks on the queue. if (syncQueue !== null) { syncQueue = syncQueue.slice(i + 1); } // Resume flushing in the next tick Scheduler_scheduleCallback(Scheduler_ImmediatePriority, flushSyncCallbackQueue); ///这里也调用了核心的scheduler方法... throw error; } finally { isFlushingSyncQueue = false; } } } }

 

//取消任务
function
unstable_cancelCallback(task) { { if (task.isQueued) { var currentTime = getCurrentTime(); markTaskCanceled(task, currentTime); task.isQueued = false; } } // Null out the callback to indicate the task has been canceled. (Can't // remove from the queue because you can't remove arbitrary nodes from an // array based heap, only the first one.) task.callback = null; }

 

posted @ 2021-07-12 22:58  AllenLiu0927  阅读(220)  评论(0)    收藏  举报