初窥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:
requestHostTimeout = function (cb, ms) { _timeoutID = setTimeout(cb, ms); };
//cb: 其中,requestHostCallback传入的是flushWork,
//这里的cb是flushWork
requestHostCallback = function (cb) { if (_callback !== null) { // Protect against re-entrancy. setTimeout(requestHostCallback, 0, cb); } else { _callback = cb; setTimeout(_flushCallback, 0); } };
//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; }

浙公网安备 33010602011771号