时间切片的实现和调度(原创2.6万字) (5)

源码文件

// 通过onmessage 调用 performWorkUntilDeadline 方法 channel.port1.onmessage = performWorkUntilDeadline; // postMessage requestHostCallback = function(callback) { scheduledHostCallback = callback; if (!isMessageLoopRunning) { isMessageLoopRunning = true; port.postMessage(null); } };

然后是同文件下的 performWorkUntilDeadline,调用了 scheduledHostCallback, 也就是之前传入的 flushWork

const performWorkUntilDeadline = () => { if (scheduledHostCallback !== null) { const currentTime = getCurrentTime(); // Yield after `yieldInterval` ms, regardless of where we are in the vsync // cycle. This means there's always time remaining at the beginning of // the message event. deadline = currentTime + yieldInterval; const hasTimeRemaining = true; try { const hasMoreWork = scheduledHostCallback( hasTimeRemaining, currentTime, ); if (!hasMoreWork) { isMessageLoopRunning = false; scheduledHostCallback = null; } else { // If there's more work, schedule the next message event at the end // of the preceding one. port.postMessage(null); } } catch (error) { // If a scheduler task throws, exit the current browser task so the // error can be observed. port.postMessage(null); throw error; } } else { isMessageLoopRunning = false; } // Yielding to the browser will give it a chance to paint, so we can // reset this. needsPaint = false; };

flushWork 主要的作用是调用 workLoop 去循环执行所有的任务

源码文件

function flushWork(hasTimeRemaining, initialTime) { if (enableProfiling) { markSchedulerUnsuspended(initialTime); } // We'll need a host callback the next time work is scheduled. isHostCallbackScheduled = false; if (isHostTimeoutScheduled) { // We scheduled a timeout but it's no longer needed. Cancel it. isHostTimeoutScheduled = false; cancelHostTimeout(); } isPerformingWork = true; const previousPriorityLevel = currentPriorityLevel; try { if (enableProfiling) { try { return workLoop(hasTimeRemaining, initialTime); } catch (error) { if (currentTask !== null) { const currentTime = getCurrentTime(); markTaskErrored(currentTask, currentTime); currentTask.isQueued = false; } throw error; } } else { // No catch in prod codepath. return workLoop(hasTimeRemaining, initialTime); } } finally { currentTask = null; currentPriorityLevel = previousPriorityLevel; isPerformingWork = false; if (enableProfiling) { const currentTime = getCurrentTime(); markSchedulerSuspended(currentTime); } } }

workLoop 和 flushWork 在一个文件中,作用是从调度任务队列中取出优先级最高的任务,然后去执行。

还记得上文讲的 SchedulerCallback 吗?

对于同步任务执行的是 performSyncWorkOnRoot

对于异步的任务执行的是 performConcurrentWorkOnRoot

function workLoop(hasTimeRemaining, initialTime) { let currentTime = initialTime; advanceTimers(currentTime); currentTask = peek(taskQueue); while ( currentTask !== null && !(enableSchedulerDebugging && isSchedulerPaused) ) { if ( currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost()) ) { // This currentTask hasn't expired, and we've reached the deadline. break; } const callback = currentTask.callback; if (callback !== null) { currentTask.callback = null; currentPriorityLevel = currentTask.priorityLevel; const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; markTaskRun(currentTask, currentTime); const continuationCallback = callback(didUserCallbackTimeout); currentTime = getCurrentTime(); if (typeof continuationCallback === 'function') { currentTask.callback = continuationCallback; markTaskYield(currentTask, currentTime); } else { if (enableProfiling) { 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 { const firstTimer = peek(timerQueue); if (firstTimer !== null) { requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); } return false; } }

最终都会通过 performUnitOfWork 操作。

这个方法只不过异步的方法是可以打断的,我们每次调用都要查看是否超时。

源码文件

function performUnitOfWork(unitOfWork: Fiber): void { // 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 = unitOfWork.alternate; setCurrentDebugFiberInDEV(unitOfWork); let next; if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) { startProfilerTimer(unitOfWork); next = beginWork(current, unitOfWork, renderExpirationTime); stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true); } else { next = beginWork(current, unitOfWork, renderExpirationTime); } resetCurrentDebugFiberInDEV(); unitOfWork.memoizedProps = unitOfWork.pendingProps; if (next === null) { // If this doesn't spawn new work, complete the current work. completeUnitOfWork(unitOfWork); } else { workInProgress = next; } ReactCurrentOwner.current = null; }

上面的 startProfilerTimer 和 stopProfilerTimerIfRunningAndRecordDelta 其实就是记录 fiber 的工作时长。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfxjf.html