[源码文件](https://github.com/facebook/react/blob/a152827ef697c55f89926f9b6b7aa436f1c0504e/packages/scheduler/src/Scheduler.js)
// 将一个任务推入任务调度队列 function unstable_scheduleCallback(priorityLevel, callback, options) { var currentTime = getCurrentTime(); var startTime; var timeout; if (typeof options === 'object' && options !== null) { var delay = options.delay; if (typeof delay === 'number' && delay > 0) { startTime = currentTime + delay; } else { startTime = currentTime; } timeout = typeof options.timeout === 'number' ? options.timeout : timeoutForPriorityLevel(priorityLevel); } else { // 针对不同的优先级算出不同的过期时间 timeout = timeoutForPriorityLevel(priorityLevel); startTime = currentTime; } // 定义新的过期时间 var expirationTime = startTime + timeout; // 定义一个新的任务 var newTask = { id: taskIdCounter++, callback, priorityLevel, startTime, expirationTime, sortIndex: -1, }; if (enableProfiling) { newTask.isQueued = false; } 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); if (enableProfiling) { 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; (flushWork); } } return newTask; }小提示: markTaskStart 主要起到记录的功能,对应的是 markTaskCompleted
源码文件
export function markTaskStart( task: { id: number, priorityLevel: PriorityLevel, ... }, ms: number, ) { if (enableProfiling) { profilingState[QUEUE_SIZE]++; if (eventLog !== null) { // performance.now returns a float, representing milliseconds. When the // event is logged, it's coerced to an int. Convert to microseconds to // maintain extra degrees of precision. logEvent([TaskStartEvent, ms * 1000, task.id, task.priorityLevel]); } } } export function markTaskCompleted( task: { id: number, priorityLevel: PriorityLevel, ... }, ms: number, ) { if (enableProfiling) { profilingState[PRIORITY] = NoPriority; profilingState[CURRENT_TASK_ID] = 0; profilingState[QUEUE_SIZE]--; if (eventLog !== null) { logEvent([TaskCompleteEvent, ms * 1000, task.id]); } } }unstable_scheduleCallback 主要做了几件事
通过 options.delay 和 options.timeout 加上 timeoutForPriorityLevel() 来获得 newTask 的 expirationTime
如果任务已过期
将超时任务推入超时队列
如果所有任务都延迟时,而且该任务是最早的任务,会调用 cancelHostTimeout
调用 requestHostTimeout
将新任务推入任务队列
源码文件
补上 cancelHostTimeout 源码
cancelHostTimeout = function() { clearTimeout(_timeoutID); };再补上 requestHostTimeout 源码
requestHostTimeout = function(cb, ms) { _timeoutID = setTimeout(cb, ms); };然后 requestHostTimeout 的 cb 也就是 handleTimeout 是啥呢?
function handleTimeout(currentTime) { isHostTimeoutScheduled = false; advanceTimers(currentTime); if (!isHostCallbackScheduled) { if (peek(taskQueue) !== null) { isHostCallbackScheduled = true; requestHostCallback(flushWork); } else { const firstTimer = peek(timerQueue); if (firstTimer !== null) { requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); } } } }上面这个方法很重要,它主要做了下面几件事
调用 advanceTimers 检查不再延迟的任务,并将其添加到队列中。
下面是 advanceTimers 源码
function advanceTimers(currentTime) { // Check for tasks that are no longer delayed and add them to the queue. let timer = peek(timerQueue); while (timer !== null) { if (timer.callback === null) { // Timer was cancelled. pop(timerQueue); } else if (timer.startTime <= currentTime) { // Timer fired. Transfer to the task queue. pop(timerQueue); timer.sortIndex = timer.expirationTime; push(taskQueue, timer); if (enableProfiling) { markTaskStart(timer, currentTime); timer.isQueued = true; } } else { // Remaining timers are pending. return; } timer = peek(timerQueue); } }调用 requestHostCallback 通过 MessageChannel 的异步方法来开启任务调度 performWorkUntilDeadline
requestHostCallback 这个方法特别重要