Use let or const to avoid scope issues and hoisting
/* eslint-disable no-var */
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910/* eslint-disable no-var */11/* eslint-disable react-internal/prod-error-codes */1213import type {PriorityLevel} from '../SchedulerPriorities';1415import {enableProfiling} from '../SchedulerFeatureFlags';16import {push, pop, peek} from '../SchedulerMinHeap';1718// TODO: Use symbols?19import {20 ImmediatePriority,21 UserBlockingPriority,22 NormalPriority,23 LowPriority,24 IdlePriority,25} from '../SchedulerPriorities';26import {27 markTaskRun,28 markTaskYield,29 markTaskCompleted,30 markTaskCanceled,31 markTaskErrored,32 markSchedulerSuspended,33 markSchedulerUnsuspended,34 markTaskStart,35 stopLoggingProfilingEvents,36 startLoggingProfilingEvents,37} from '../SchedulerProfiling';3839type Callback = boolean => ?Callback;4041type Task = {42 id: number,43 callback: Callback | null,44 priorityLevel: PriorityLevel,45 startTime: number,46 expirationTime: number,47 sortIndex: number,48 isQueued?: boolean,49};5051// Max 31 bit integer. The max integer size in V8 for 32-bit systems.52// Math.pow(2, 30) - 153// 0b11111111111111111111111111111154var maxSigned31BitInt = 1073741823;5556// Times out immediately57var IMMEDIATE_PRIORITY_TIMEOUT = -1;58// Eventually times out59var USER_BLOCKING_PRIORITY_TIMEOUT = 250;60var NORMAL_PRIORITY_TIMEOUT = 5000;61var LOW_PRIORITY_TIMEOUT = 10000;62// Never times out63var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;6465// Tasks are stored on a min heap66var taskQueue: Array<Task> = [];67var timerQueue: Array<Task> = [];6869// Incrementing id counter. Used to maintain insertion order.70var taskIdCounter = 1;7172var currentTask = null;73var currentPriorityLevel: PriorityLevel = NormalPriority;7475// This is set while performing work, to prevent re-entrance.76var isPerformingWork = false;7778var isHostCallbackScheduled = false;79var isHostTimeoutScheduled = false;8081let currentMockTime: number = 0;82let scheduledCallback:83 | null84 | ((85 hasTimeRemaining: boolean,86 initialTime: DOMHighResTimeStamp | number,87 ) => boolean) = null;88let scheduledTimeout: (number => void) | null = null;89let timeoutTime: number = -1;90let yieldedValues: Array<mixed> | null = null;91let expectedNumberOfYields: number = -1;92let didStop: boolean = false;93let isFlushing: boolean = false;94let needsPaint: boolean = false;95let shouldYieldForPaint: boolean = false;9697var disableYieldValue = false;9899function setDisableYieldValue(newValue: boolean) {100 disableYieldValue = newValue;101}102103function advanceTimers(currentTime: number) {104 // Check for tasks that are no longer delayed and add them to the queue.105 let timer = peek(timerQueue);106 while (timer !== null) {107 if (timer.callback === null) {108 // Timer was cancelled.109 pop(timerQueue);110 } else if (timer.startTime <= currentTime) {111 // Timer fired. Transfer to the task queue.112 pop(timerQueue);113 timer.sortIndex = timer.expirationTime;114 push(taskQueue, timer);115 // $FlowFixMe[constant-condition]116 if (enableProfiling) {117 markTaskStart(timer, currentTime);118 timer.isQueued = true;119 }120 } else {121 // Remaining timers are pending.122 return;123 }124 timer = peek(timerQueue);125 }126}127128function handleTimeout(currentTime: number) {129 isHostTimeoutScheduled = false;130 advanceTimers(currentTime);131132 if (!isHostCallbackScheduled) {133 if (peek(taskQueue) !== null) {134 isHostCallbackScheduled = true;135 requestHostCallback(flushWork);136 } else {137 const firstTimer = peek(timerQueue);138 if (firstTimer !== null) {139 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);140 }141 }142 }143}144145function flushWork(hasTimeRemaining: boolean, initialTime: number) {146 // $FlowFixMe[constant-condition]147 if (enableProfiling) {148 markSchedulerUnsuspended(initialTime);149 }150151 // We'll need a host callback the next time work is scheduled.152 isHostCallbackScheduled = false;153 if (isHostTimeoutScheduled) {154 // We scheduled a timeout but it's no longer needed. Cancel it.155 isHostTimeoutScheduled = false;156 cancelHostTimeout();157 }158159 isPerformingWork = true;160 const previousPriorityLevel = currentPriorityLevel;161 try {162 // $FlowFixMe[constant-condition]163 if (enableProfiling) {164 try {165 return workLoop(hasTimeRemaining, initialTime);166 } catch (error) {167 if (currentTask !== null) {168 const currentTime = getCurrentTime();169 // $FlowFixMe[incompatible-call] found when upgrading Flow170 // $FlowFixMe[incompatible-type]171 markTaskErrored(currentTask, currentTime);172 // $FlowFixMe[incompatible-use] found when upgrading Flow173 currentTask.isQueued = false;174 }175 throw error;176 }177 } else {178 // No catch in prod code path.179 return workLoop(hasTimeRemaining, initialTime);180 }181 } finally {182 currentTask = null;183 currentPriorityLevel = previousPriorityLevel;184 isPerformingWork = false;185 // $FlowFixMe[constant-condition]186 if (enableProfiling) {187 const currentTime = getCurrentTime();188 markSchedulerSuspended(currentTime);189 }190 }191}192193function workLoop(hasTimeRemaining: boolean, initialTime: number): boolean {194 let currentTime = initialTime;195 advanceTimers(currentTime);196 currentTask = peek(taskQueue);197 while (currentTask !== null) {198 if (199 currentTask.expirationTime > currentTime &&200 (!hasTimeRemaining || shouldYieldToHost())201 ) {202 // This currentTask hasn't expired, and we've reached the deadline.203 break;204 }205 // $FlowFixMe[incompatible-use] found when upgrading Flow206 const callback = currentTask.callback;207 if (typeof callback === 'function') {208 // $FlowFixMe[incompatible-use] found when upgrading Flow209 currentTask.callback = null;210 // $FlowFixMe[incompatible-use] found when upgrading Flow211 currentPriorityLevel = currentTask.priorityLevel;212 // $FlowFixMe[incompatible-use] found when upgrading Flow213 const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;214 // $FlowFixMe[constant-condition]215 if (enableProfiling) {216 // $FlowFixMe[incompatible-call] found when upgrading Flow217 // $FlowFixMe[incompatible-type]218 markTaskRun(currentTask, currentTime);219 }220 const continuationCallback = callback(didUserCallbackTimeout);221 currentTime = getCurrentTime();222 if (typeof continuationCallback === 'function') {223 // If a continuation is returned, immediately yield to the main thread224 // regardless of how much time is left in the current time slice.225 // $FlowFixMe[incompatible-use] found when upgrading Flow226 currentTask.callback = continuationCallback;227 // $FlowFixMe[constant-condition]228 if (enableProfiling) {229 // $FlowFixMe[incompatible-call] found when upgrading Flow230 // $FlowFixMe[incompatible-type]231 markTaskYield(currentTask, currentTime);232 }233 advanceTimers(currentTime);234235 if (shouldYieldForPaint) {236 needsPaint = true;237 return true;238 } else {239 // If `shouldYieldForPaint` is false, we keep flushing synchronously240 // without yielding to the main thread. This is the behavior of the241 // `toFlushAndYield` and `toFlushAndYieldThrough` testing helpers .242 }243 } else {244 // $FlowFixMe[constant-condition]245 if (enableProfiling) {246 // $FlowFixMe[incompatible-call] found when upgrading Flow247 // $FlowFixMe[incompatible-type]248 markTaskCompleted(currentTask, currentTime);249 // $FlowFixMe[incompatible-use] found when upgrading Flow250 currentTask.isQueued = false;251 }252 if (currentTask === peek(taskQueue)) {253 pop(taskQueue);254 }255 advanceTimers(currentTime);256 }257 } else {258 pop(taskQueue);259 }260 currentTask = peek(taskQueue);261 }262 // Return whether there's additional work263 if (currentTask !== null) {264 return true;265 } else {266 const firstTimer = peek(timerQueue);267 if (firstTimer !== null) {268 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);269 }270 return false;271 }272}273274function unstable_runWithPriority<T>(275 priorityLevel: PriorityLevel,276 eventHandler: () => T,277): T {278 switch (priorityLevel) {279 case ImmediatePriority:280 case UserBlockingPriority:281 case NormalPriority:282 case LowPriority:283 case IdlePriority:284 break;285 default:286 priorityLevel = NormalPriority;287 }288289 var previousPriorityLevel = currentPriorityLevel;290 currentPriorityLevel = priorityLevel;291292 try {293 return eventHandler();294 } finally {295 currentPriorityLevel = previousPriorityLevel;296 }297}298299function unstable_next<T>(eventHandler: () => T): T {300 var priorityLevel: PriorityLevel;301 switch (currentPriorityLevel) {302 case ImmediatePriority:303 case UserBlockingPriority:304 case NormalPriority:305 // Shift down to normal priority306 priorityLevel = NormalPriority;307 break;308 default:309 // Anything lower than normal priority should remain at the current level.310 priorityLevel = currentPriorityLevel;311 break;312 }313314 var previousPriorityLevel = currentPriorityLevel;315 currentPriorityLevel = priorityLevel;316317 try {318 return eventHandler();319 } finally {320 currentPriorityLevel = previousPriorityLevel;321 }322}323324function unstable_wrapCallback<T: (...Array<mixed>) => mixed>(callback: T): T {325 var parentPriorityLevel = currentPriorityLevel;326 // $FlowFixMe[incompatible-return]327 // $FlowFixMe[missing-this-annot]328 // $FlowFixMe[incompatible-type]329 return function () {330 // This is a fork of runWithPriority, inlined for performance.331 var previousPriorityLevel = currentPriorityLevel;332 currentPriorityLevel = parentPriorityLevel;333334 try {335 return callback.apply(this, arguments);336 } finally {337 currentPriorityLevel = previousPriorityLevel;338 }339 };340}341342function unstable_scheduleCallback(343 priorityLevel: PriorityLevel,344 callback: Callback,345 options?: {delay: number},346): Task {347 var currentTime = getCurrentTime();348349 var startTime;350 // $FlowFixMe[invalid-compare]351 if (typeof options === 'object' && options !== null) {352 var delay = options.delay;353 if (typeof delay === 'number' && delay > 0) {354 startTime = currentTime + delay;355 } else {356 startTime = currentTime;357 }358 } else {359 startTime = currentTime;360 }361362 var timeout;363 switch (priorityLevel) {364 case ImmediatePriority:365 timeout = IMMEDIATE_PRIORITY_TIMEOUT;366 break;367 case UserBlockingPriority:368 timeout = USER_BLOCKING_PRIORITY_TIMEOUT;369 break;370 case IdlePriority:371 timeout = IDLE_PRIORITY_TIMEOUT;372 break;373 case LowPriority:374 timeout = LOW_PRIORITY_TIMEOUT;375 break;376 case NormalPriority:377 default:378 timeout = NORMAL_PRIORITY_TIMEOUT;379 break;380 }381382 var expirationTime = startTime + timeout;383384 var newTask: Task = {385 id: taskIdCounter++,386 callback,387 priorityLevel,388 startTime,389 expirationTime,390 sortIndex: -1,391 };392 // $FlowFixMe[constant-condition]393 if (enableProfiling) {394 newTask.isQueued = false;395 }396397 if (startTime > currentTime) {398 // This is a delayed task.399 newTask.sortIndex = startTime;400 push(timerQueue, newTask);401 if (peek(taskQueue) === null && newTask === peek(timerQueue)) {402 // All tasks are delayed, and this is the task with the earliest delay.403 if (isHostTimeoutScheduled) {404 // Cancel an existing timeout.405 cancelHostTimeout();406 } else {407 isHostTimeoutScheduled = true;408 }409 // Schedule a timeout.410 requestHostTimeout(handleTimeout, startTime - currentTime);411 }412 } else {413 newTask.sortIndex = expirationTime;414 push(taskQueue, newTask);415 // $FlowFixMe[constant-condition]416 if (enableProfiling) {417 markTaskStart(newTask, currentTime);418 newTask.isQueued = true;419 }420 // Schedule a host callback, if needed. If we're already performing work,421 // wait until the next time we yield.422 if (!isHostCallbackScheduled && !isPerformingWork) {423 isHostCallbackScheduled = true;424 requestHostCallback(flushWork);425 }426 }427428 return newTask;429}430431function unstable_cancelCallback(task: Task) {432 // $FlowFixMe[constant-condition]433 if (enableProfiling) {434 if (task.isQueued) {435 const currentTime = getCurrentTime();436 markTaskCanceled(task, currentTime);437 task.isQueued = false;438 }439 }440441 // Null out the callback to indicate the task has been canceled. (Can't442 // remove from the queue because you can't remove arbitrary nodes from an443 // array based heap, only the first one.)444 task.callback = null;445}446447function unstable_getCurrentPriorityLevel(): PriorityLevel {448 return currentPriorityLevel;449}450451function requestHostCallback(callback: (boolean, number) => boolean) {452 scheduledCallback = callback;453}454455function requestHostTimeout(callback: number => void, ms: number) {456 scheduledTimeout = callback;457 timeoutTime = currentMockTime + ms;458}459460function cancelHostTimeout(): void {461 scheduledTimeout = null;462 timeoutTime = -1;463}464465function shouldYieldToHost(): boolean {466 if (467 (expectedNumberOfYields === 0 && yieldedValues === null) ||468 (expectedNumberOfYields !== -1 &&469 yieldedValues !== null &&470 yieldedValues.length >= expectedNumberOfYields) ||471 (shouldYieldForPaint && needsPaint)472 ) {473 // We yielded at least as many values as expected. Stop flushing.474 didStop = true;475 return true;476 }477 return false;478}479480function getCurrentTime(): number {481 return currentMockTime;482}483484function forceFrameRate() {485 // No-op486}487488function reset() {489 if (isFlushing) {490 throw new Error('Cannot reset while already flushing work.');491 }492 currentMockTime = 0;493 scheduledCallback = null;494 scheduledTimeout = null;495 timeoutTime = -1;496 yieldedValues = null;497 expectedNumberOfYields = -1;498 didStop = false;499 isFlushing = false;500 needsPaint = false;501}502503// Should only be used via an assertion helper that inspects the yielded values.504function unstable_flushNumberOfYields(count: number): void {505 if (isFlushing) {506 throw new Error('Already flushing work.');507 }508 if (scheduledCallback !== null) {509 const cb = scheduledCallback;510 expectedNumberOfYields = count;511 isFlushing = true;512 try {513 let hasMoreWork = true;514 do {515 hasMoreWork = cb(true, currentMockTime);516 } while (hasMoreWork && !didStop);517 if (!hasMoreWork) {518 scheduledCallback = null;519 }520 } finally {521 expectedNumberOfYields = -1;522 didStop = false;523 isFlushing = false;524 }525 }526}527528function unstable_flushUntilNextPaint(): false {529 if (isFlushing) {530 throw new Error('Already flushing work.');531 }532 if (scheduledCallback !== null) {533 const cb = scheduledCallback;534 shouldYieldForPaint = true;535 needsPaint = false;536 isFlushing = true;537 try {538 let hasMoreWork = true;539 do {540 hasMoreWork = cb(true, currentMockTime);541 } while (hasMoreWork && !didStop);542 if (!hasMoreWork) {543 scheduledCallback = null;544 }545 } finally {546 shouldYieldForPaint = false;547 didStop = false;548 isFlushing = false;549 }550 }551 return false;552}553554function unstable_hasPendingWork(): boolean {555 return scheduledCallback !== null;556}557558function unstable_flushExpired() {559 if (isFlushing) {560 throw new Error('Already flushing work.');561 }562 if (scheduledCallback !== null) {563 isFlushing = true;564 try {565 const hasMoreWork = scheduledCallback(false, currentMockTime);566 if (!hasMoreWork) {567 scheduledCallback = null;568 }569 } finally {570 isFlushing = false;571 }572 }573}574575function unstable_flushAllWithoutAsserting(): boolean {576 // Returns false if no work was flushed.577 if (isFlushing) {578 throw new Error('Already flushing work.');579 }580 if (scheduledCallback !== null) {581 const cb = scheduledCallback;582 isFlushing = true;583 try {584 let hasMoreWork = true;585 do {586 hasMoreWork = cb(true, currentMockTime);587 } while (hasMoreWork);588 // $FlowFixMe[constant-condition]589 if (!hasMoreWork) {590 scheduledCallback = null;591 }592 return true;593 } finally {594 isFlushing = false;595 }596 } else {597 return false;598 }599}600601function unstable_clearLog(): Array<mixed> {602 if (yieldedValues === null) {603 return [];604 }605 const values = yieldedValues;606 yieldedValues = null;607 return values;608}609610function unstable_flushAll(): void {611 if (yieldedValues !== null) {612 throw new Error(613 'Log is not empty. Assert on the log of yielded values before ' +614 'flushing additional work.',615 );616 }617 unstable_flushAllWithoutAsserting();618 if (yieldedValues !== null) {619 throw new Error(620 'While flushing work, something yielded a value. Use an ' +621 'assertion helper to assert on the log of yielded values, e.g. ' +622 'expect(Scheduler).toFlushAndYield([...])',623 );624 }625}626627function log(value: mixed): void {628 // eslint-disable-next-line react-internal/no-production-logging629 if (console.log.name === 'disabledLog' || disableYieldValue) {630 // If console.log has been patched, we assume we're in render631 // replaying and we ignore any values yielding in the second pass.632 return;633 }634 if (yieldedValues === null) {635 yieldedValues = [value];636 } else {637 yieldedValues.push(value);638 }639}640641function unstable_advanceTime(ms: number) {642 // eslint-disable-next-line react-internal/no-production-logging643 if (console.log.name === 'disabledLog' || disableYieldValue) {644 // If console.log has been patched, we assume we're in render645 // replaying and we ignore any time advancing in the second pass.646 return;647 }648 currentMockTime += ms;649 if (scheduledTimeout !== null && timeoutTime <= currentMockTime) {650 scheduledTimeout(currentMockTime);651 timeoutTime = -1;652 scheduledTimeout = null;653 }654}655656function requestPaint() {657 needsPaint = true;658}659660export {661 ImmediatePriority as unstable_ImmediatePriority,662 UserBlockingPriority as unstable_UserBlockingPriority,663 NormalPriority as unstable_NormalPriority,664 IdlePriority as unstable_IdlePriority,665 LowPriority as unstable_LowPriority,666 unstable_runWithPriority,667 unstable_next,668 unstable_scheduleCallback,669 unstable_cancelCallback,670 unstable_wrapCallback,671 unstable_getCurrentPriorityLevel,672 shouldYieldToHost as unstable_shouldYield,673 requestPaint as unstable_requestPaint,674 getCurrentTime as unstable_now,675 forceFrameRate as unstable_forceFrameRate,676 unstable_flushAllWithoutAsserting,677 unstable_flushNumberOfYields,678 unstable_flushExpired,679 unstable_clearLog,680 unstable_flushUntilNextPaint,681 unstable_hasPendingWork,682 unstable_flushAll,683 log,684 unstable_advanceTime,685 reset,686 setDisableYieldValue as unstable_setDisableYieldValue,687};688689export const unstable_Profiling: {690 startLoggingProfilingEvents(): void,691 stopLoggingProfilingEvents(): ArrayBuffer | null,692 // $FlowFixMe[constant-condition]693} | null = enableProfiling694 ? {695 startLoggingProfilingEvents,696 stopLoggingProfilingEvents,697 }698 : null;
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.