scheduler.development.js (17497B)
1/** 2 * @license React 3 * scheduler.development.js 4 * 5 * Copyright (c) Facebook, Inc. and its affiliates. 6 * 7 * This source code is licensed under the MIT license found in the 8 * LICENSE file in the root directory of this source tree. 9 */ 10 11'use strict'; 12 13if (process.env.NODE_ENV !== "production") { 14 (function() { 15 16 'use strict'; 17 18/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ 19if ( 20 typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && 21 typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart === 22 'function' 23) { 24 __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error()); 25} 26 var enableSchedulerDebugging = false; 27var enableProfiling = false; 28var frameYieldMs = 5; 29 30function push(heap, node) { 31 var index = heap.length; 32 heap.push(node); 33 siftUp(heap, node, index); 34} 35function peek(heap) { 36 return heap.length === 0 ? null : heap[0]; 37} 38function pop(heap) { 39 if (heap.length === 0) { 40 return null; 41 } 42 43 var first = heap[0]; 44 var last = heap.pop(); 45 46 if (last !== first) { 47 heap[0] = last; 48 siftDown(heap, last, 0); 49 } 50 51 return first; 52} 53 54function siftUp(heap, node, i) { 55 var index = i; 56 57 while (index > 0) { 58 var parentIndex = index - 1 >>> 1; 59 var parent = heap[parentIndex]; 60 61 if (compare(parent, node) > 0) { 62 // The parent is larger. Swap positions. 63 heap[parentIndex] = node; 64 heap[index] = parent; 65 index = parentIndex; 66 } else { 67 // The parent is smaller. Exit. 68 return; 69 } 70 } 71} 72 73function siftDown(heap, node, i) { 74 var index = i; 75 var length = heap.length; 76 var halfLength = length >>> 1; 77 78 while (index < halfLength) { 79 var leftIndex = (index + 1) * 2 - 1; 80 var left = heap[leftIndex]; 81 var rightIndex = leftIndex + 1; 82 var right = heap[rightIndex]; // If the left or right node is smaller, swap with the smaller of those. 83 84 if (compare(left, node) < 0) { 85 if (rightIndex < length && compare(right, left) < 0) { 86 heap[index] = right; 87 heap[rightIndex] = node; 88 index = rightIndex; 89 } else { 90 heap[index] = left; 91 heap[leftIndex] = node; 92 index = leftIndex; 93 } 94 } else if (rightIndex < length && compare(right, node) < 0) { 95 heap[index] = right; 96 heap[rightIndex] = node; 97 index = rightIndex; 98 } else { 99 // Neither child is smaller. Exit. 100 return; 101 } 102 } 103} 104 105function compare(a, b) { 106 // Compare sort index first, then task id. 107 var diff = a.sortIndex - b.sortIndex; 108 return diff !== 0 ? diff : a.id - b.id; 109} 110 111// TODO: Use symbols? 112var ImmediatePriority = 1; 113var UserBlockingPriority = 2; 114var NormalPriority = 3; 115var LowPriority = 4; 116var IdlePriority = 5; 117 118function markTaskErrored(task, ms) { 119} 120 121/* eslint-disable no-var */ 122 123var hasPerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; 124 125if (hasPerformanceNow) { 126 var localPerformance = performance; 127 128 exports.unstable_now = function () { 129 return localPerformance.now(); 130 }; 131} else { 132 var localDate = Date; 133 var initialTime = localDate.now(); 134 135 exports.unstable_now = function () { 136 return localDate.now() - initialTime; 137 }; 138} // Max 31 bit integer. The max integer size in V8 for 32-bit systems. 139// Math.pow(2, 30) - 1 140// 0b111111111111111111111111111111 141 142 143var maxSigned31BitInt = 1073741823; // Times out immediately 144 145var IMMEDIATE_PRIORITY_TIMEOUT = -1; // Eventually times out 146 147var USER_BLOCKING_PRIORITY_TIMEOUT = 250; 148var NORMAL_PRIORITY_TIMEOUT = 5000; 149var LOW_PRIORITY_TIMEOUT = 10000; // Never times out 150 151var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt; // Tasks are stored on a min heap 152 153var taskQueue = []; 154var timerQueue = []; // Incrementing id counter. Used to maintain insertion order. 155 156var taskIdCounter = 1; // Pausing the scheduler is useful for debugging. 157var currentTask = null; 158var currentPriorityLevel = NormalPriority; // This is set while performing work, to prevent re-entrance. 159 160var isPerformingWork = false; 161var isHostCallbackScheduled = false; 162var isHostTimeoutScheduled = false; // Capture local references to native APIs, in case a polyfill overrides them. 163 164var localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null; 165var localClearTimeout = typeof clearTimeout === 'function' ? clearTimeout : null; 166var localSetImmediate = typeof setImmediate !== 'undefined' ? setImmediate : null; // IE and Node.js + jsdom 167 168var isInputPending = typeof navigator !== 'undefined' && navigator.scheduling !== undefined && navigator.scheduling.isInputPending !== undefined ? navigator.scheduling.isInputPending.bind(navigator.scheduling) : null; 169 170function advanceTimers(currentTime) { 171 // Check for tasks that are no longer delayed and add them to the queue. 172 var timer = peek(timerQueue); 173 174 while (timer !== null) { 175 if (timer.callback === null) { 176 // Timer was cancelled. 177 pop(timerQueue); 178 } else if (timer.startTime <= currentTime) { 179 // Timer fired. Transfer to the task queue. 180 pop(timerQueue); 181 timer.sortIndex = timer.expirationTime; 182 push(taskQueue, timer); 183 } else { 184 // Remaining timers are pending. 185 return; 186 } 187 188 timer = peek(timerQueue); 189 } 190} 191 192function handleTimeout(currentTime) { 193 isHostTimeoutScheduled = false; 194 advanceTimers(currentTime); 195 196 if (!isHostCallbackScheduled) { 197 if (peek(taskQueue) !== null) { 198 isHostCallbackScheduled = true; 199 requestHostCallback(flushWork); 200 } else { 201 var firstTimer = peek(timerQueue); 202 203 if (firstTimer !== null) { 204 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); 205 } 206 } 207 } 208} 209 210function flushWork(hasTimeRemaining, initialTime) { 211 212 213 isHostCallbackScheduled = false; 214 215 if (isHostTimeoutScheduled) { 216 // We scheduled a timeout but it's no longer needed. Cancel it. 217 isHostTimeoutScheduled = false; 218 cancelHostTimeout(); 219 } 220 221 isPerformingWork = true; 222 var previousPriorityLevel = currentPriorityLevel; 223 224 try { 225 if (enableProfiling) { 226 try { 227 return workLoop(hasTimeRemaining, initialTime); 228 } catch (error) { 229 if (currentTask !== null) { 230 var currentTime = exports.unstable_now(); 231 markTaskErrored(currentTask, currentTime); 232 currentTask.isQueued = false; 233 } 234 235 throw error; 236 } 237 } else { 238 // No catch in prod code path. 239 return workLoop(hasTimeRemaining, initialTime); 240 } 241 } finally { 242 currentTask = null; 243 currentPriorityLevel = previousPriorityLevel; 244 isPerformingWork = false; 245 } 246} 247 248function workLoop(hasTimeRemaining, initialTime) { 249 var currentTime = initialTime; 250 advanceTimers(currentTime); 251 currentTask = peek(taskQueue); 252 253 while (currentTask !== null && !(enableSchedulerDebugging )) { 254 if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost())) { 255 // This currentTask hasn't expired, and we've reached the deadline. 256 break; 257 } 258 259 var callback = currentTask.callback; 260 261 if (typeof callback === 'function') { 262 currentTask.callback = null; 263 currentPriorityLevel = currentTask.priorityLevel; 264 var didUserCallbackTimeout = currentTask.expirationTime <= currentTime; 265 266 var continuationCallback = callback(didUserCallbackTimeout); 267 currentTime = exports.unstable_now(); 268 269 if (typeof continuationCallback === 'function') { 270 currentTask.callback = continuationCallback; 271 } else { 272 273 if (currentTask === peek(taskQueue)) { 274 pop(taskQueue); 275 } 276 } 277 278 advanceTimers(currentTime); 279 } else { 280 pop(taskQueue); 281 } 282 283 currentTask = peek(taskQueue); 284 } // Return whether there's additional work 285 286 287 if (currentTask !== null) { 288 return true; 289 } else { 290 var firstTimer = peek(timerQueue); 291 292 if (firstTimer !== null) { 293 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); 294 } 295 296 return false; 297 } 298} 299 300function unstable_runWithPriority(priorityLevel, eventHandler) { 301 switch (priorityLevel) { 302 case ImmediatePriority: 303 case UserBlockingPriority: 304 case NormalPriority: 305 case LowPriority: 306 case IdlePriority: 307 break; 308 309 default: 310 priorityLevel = NormalPriority; 311 } 312 313 var previousPriorityLevel = currentPriorityLevel; 314 currentPriorityLevel = priorityLevel; 315 316 try { 317 return eventHandler(); 318 } finally { 319 currentPriorityLevel = previousPriorityLevel; 320 } 321} 322 323function unstable_next(eventHandler) { 324 var priorityLevel; 325 326 switch (currentPriorityLevel) { 327 case ImmediatePriority: 328 case UserBlockingPriority: 329 case NormalPriority: 330 // Shift down to normal priority 331 priorityLevel = NormalPriority; 332 break; 333 334 default: 335 // Anything lower than normal priority should remain at the current level. 336 priorityLevel = currentPriorityLevel; 337 break; 338 } 339 340 var previousPriorityLevel = currentPriorityLevel; 341 currentPriorityLevel = priorityLevel; 342 343 try { 344 return eventHandler(); 345 } finally { 346 currentPriorityLevel = previousPriorityLevel; 347 } 348} 349 350function unstable_wrapCallback(callback) { 351 var parentPriorityLevel = currentPriorityLevel; 352 return function () { 353 // This is a fork of runWithPriority, inlined for performance. 354 var previousPriorityLevel = currentPriorityLevel; 355 currentPriorityLevel = parentPriorityLevel; 356 357 try { 358 return callback.apply(this, arguments); 359 } finally { 360 currentPriorityLevel = previousPriorityLevel; 361 } 362 }; 363} 364 365function unstable_scheduleCallback(priorityLevel, callback, options) { 366 var currentTime = exports.unstable_now(); 367 var startTime; 368 369 if (typeof options === 'object' && options !== null) { 370 var delay = options.delay; 371 372 if (typeof delay === 'number' && delay > 0) { 373 startTime = currentTime + delay; 374 } else { 375 startTime = currentTime; 376 } 377 } else { 378 startTime = currentTime; 379 } 380 381 var timeout; 382 383 switch (priorityLevel) { 384 case ImmediatePriority: 385 timeout = IMMEDIATE_PRIORITY_TIMEOUT; 386 break; 387 388 case UserBlockingPriority: 389 timeout = USER_BLOCKING_PRIORITY_TIMEOUT; 390 break; 391 392 case IdlePriority: 393 timeout = IDLE_PRIORITY_TIMEOUT; 394 break; 395 396 case LowPriority: 397 timeout = LOW_PRIORITY_TIMEOUT; 398 break; 399 400 case NormalPriority: 401 default: 402 timeout = NORMAL_PRIORITY_TIMEOUT; 403 break; 404 } 405 406 var expirationTime = startTime + timeout; 407 var newTask = { 408 id: taskIdCounter++, 409 callback: callback, 410 priorityLevel: priorityLevel, 411 startTime: startTime, 412 expirationTime: expirationTime, 413 sortIndex: -1 414 }; 415 416 if (startTime > currentTime) { 417 // This is a delayed task. 418 newTask.sortIndex = startTime; 419 push(timerQueue, newTask); 420 421 if (peek(taskQueue) === null && newTask === peek(timerQueue)) { 422 // All tasks are delayed, and this is the task with the earliest delay. 423 if (isHostTimeoutScheduled) { 424 // Cancel an existing timeout. 425 cancelHostTimeout(); 426 } else { 427 isHostTimeoutScheduled = true; 428 } // Schedule a timeout. 429 430 431 requestHostTimeout(handleTimeout, startTime - currentTime); 432 } 433 } else { 434 newTask.sortIndex = expirationTime; 435 push(taskQueue, newTask); 436 // wait until the next time we yield. 437 438 439 if (!isHostCallbackScheduled && !isPerformingWork) { 440 isHostCallbackScheduled = true; 441 requestHostCallback(flushWork); 442 } 443 } 444 445 return newTask; 446} 447 448function unstable_pauseExecution() { 449} 450 451function unstable_continueExecution() { 452 453 if (!isHostCallbackScheduled && !isPerformingWork) { 454 isHostCallbackScheduled = true; 455 requestHostCallback(flushWork); 456 } 457} 458 459function unstable_getFirstCallbackNode() { 460 return peek(taskQueue); 461} 462 463function unstable_cancelCallback(task) { 464 // remove from the queue because you can't remove arbitrary nodes from an 465 // array based heap, only the first one.) 466 467 468 task.callback = null; 469} 470 471function unstable_getCurrentPriorityLevel() { 472 return currentPriorityLevel; 473} 474 475var isMessageLoopRunning = false; 476var scheduledHostCallback = null; 477var taskTimeoutID = -1; // Scheduler periodically yields in case there is other work on the main 478// thread, like user events. By default, it yields multiple times per frame. 479// It does not attempt to align with frame boundaries, since most tasks don't 480// need to be frame aligned; for those that do, use requestAnimationFrame. 481 482var frameInterval = frameYieldMs; 483var startTime = -1; 484 485function shouldYieldToHost() { 486 var timeElapsed = exports.unstable_now() - startTime; 487 488 if (timeElapsed < frameInterval) { 489 // The main thread has only been blocked for a really short amount of time; 490 // smaller than a single frame. Don't yield yet. 491 return false; 492 } // The main thread has been blocked for a non-negligible amount of time. We 493 494 495 return true; 496} 497 498function requestPaint() { 499 500} 501 502function forceFrameRate(fps) { 503 if (fps < 0 || fps > 125) { 504 // Using console['error'] to evade Babel and ESLint 505 console['error']('forceFrameRate takes a positive int between 0 and 125, ' + 'forcing frame rates higher than 125 fps is not supported'); 506 return; 507 } 508 509 if (fps > 0) { 510 frameInterval = Math.floor(1000 / fps); 511 } else { 512 // reset the framerate 513 frameInterval = frameYieldMs; 514 } 515} 516 517var performWorkUntilDeadline = function () { 518 if (scheduledHostCallback !== null) { 519 var currentTime = exports.unstable_now(); // Keep track of the start time so we can measure how long the main thread 520 // has been blocked. 521 522 startTime = currentTime; 523 var hasTimeRemaining = true; // If a scheduler task throws, exit the current browser task so the 524 // error can be observed. 525 // 526 // Intentionally not using a try-catch, since that makes some debugging 527 // techniques harder. Instead, if `scheduledHostCallback` errors, then 528 // `hasMoreWork` will remain true, and we'll continue the work loop. 529 530 var hasMoreWork = true; 531 532 try { 533 hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime); 534 } finally { 535 if (hasMoreWork) { 536 // If there's more work, schedule the next message event at the end 537 // of the preceding one. 538 schedulePerformWorkUntilDeadline(); 539 } else { 540 isMessageLoopRunning = false; 541 scheduledHostCallback = null; 542 } 543 } 544 } else { 545 isMessageLoopRunning = false; 546 } // Yielding to the browser will give it a chance to paint, so we can 547}; 548 549var schedulePerformWorkUntilDeadline; 550 551if (typeof localSetImmediate === 'function') { 552 // Node.js and old IE. 553 // There's a few reasons for why we prefer setImmediate. 554 // 555 // Unlike MessageChannel, it doesn't prevent a Node.js process from exiting. 556 // (Even though this is a DOM fork of the Scheduler, you could get here 557 // with a mix of Node.js 15+, which has a MessageChannel, and jsdom.) 558 // https://github.com/facebook/react/issues/20756 559 // 560 // But also, it runs earlier which is the semantic we want. 561 // If other browsers ever implement it, it's better to use it. 562 // Although both of these would be inferior to native scheduling. 563 schedulePerformWorkUntilDeadline = function () { 564 localSetImmediate(performWorkUntilDeadline); 565 }; 566} else if (typeof MessageChannel !== 'undefined') { 567 // DOM and Worker environments. 568 // We prefer MessageChannel because of the 4ms setTimeout clamping. 569 var channel = new MessageChannel(); 570 var port = channel.port2; 571 channel.port1.onmessage = performWorkUntilDeadline; 572 573 schedulePerformWorkUntilDeadline = function () { 574 port.postMessage(null); 575 }; 576} else { 577 // We should only fallback here in non-browser environments. 578 schedulePerformWorkUntilDeadline = function () { 579 localSetTimeout(performWorkUntilDeadline, 0); 580 }; 581} 582 583function requestHostCallback(callback) { 584 scheduledHostCallback = callback; 585 586 if (!isMessageLoopRunning) { 587 isMessageLoopRunning = true; 588 schedulePerformWorkUntilDeadline(); 589 } 590} 591 592function requestHostTimeout(callback, ms) { 593 taskTimeoutID = localSetTimeout(function () { 594 callback(exports.unstable_now()); 595 }, ms); 596} 597 598function cancelHostTimeout() { 599 localClearTimeout(taskTimeoutID); 600 taskTimeoutID = -1; 601} 602 603var unstable_requestPaint = requestPaint; 604var unstable_Profiling = null; 605 606exports.unstable_IdlePriority = IdlePriority; 607exports.unstable_ImmediatePriority = ImmediatePriority; 608exports.unstable_LowPriority = LowPriority; 609exports.unstable_NormalPriority = NormalPriority; 610exports.unstable_Profiling = unstable_Profiling; 611exports.unstable_UserBlockingPriority = UserBlockingPriority; 612exports.unstable_cancelCallback = unstable_cancelCallback; 613exports.unstable_continueExecution = unstable_continueExecution; 614exports.unstable_forceFrameRate = forceFrameRate; 615exports.unstable_getCurrentPriorityLevel = unstable_getCurrentPriorityLevel; 616exports.unstable_getFirstCallbackNode = unstable_getFirstCallbackNode; 617exports.unstable_next = unstable_next; 618exports.unstable_pauseExecution = unstable_pauseExecution; 619exports.unstable_requestPaint = unstable_requestPaint; 620exports.unstable_runWithPriority = unstable_runWithPriority; 621exports.unstable_scheduleCallback = unstable_scheduleCallback; 622exports.unstable_shouldYield = shouldYieldToHost; 623exports.unstable_wrapCallback = unstable_wrapCallback; 624 /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ 625if ( 626 typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && 627 typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop === 628 'function' 629) { 630 __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error()); 631} 632 633 })(); 634}