UNPKG

25.6 kBJavaScriptView Raw
1/** @license React v0.18.0
2 * scheduler.development.js
3 *
4 * Copyright (c) Facebook, Inc. and its affiliates.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE file in the root directory of this source tree.
8 */
9
10'use strict';
11
12
13
14if (process.env.NODE_ENV !== "production") {
15 (function() {
16'use strict';
17
18Object.defineProperty(exports, '__esModule', { value: true });
19
20var enableSchedulerDebugging = false;
21var enableIsInputPending = false;
22var enableProfiling = true;
23
24var requestHostCallback;
25
26var requestHostTimeout;
27var cancelHostTimeout;
28var shouldYieldToHost;
29var requestPaint;
30
31
32
33if ( // If Scheduler runs in a non-DOM environment, it falls back to a naive
34// implementation using setTimeout.
35typeof window === 'undefined' || // Check if MessageChannel is supported, too.
36typeof MessageChannel !== 'function') {
37 // If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
38 // fallback to a naive implementation.
39 var _callback = null;
40 var _timeoutID = null;
41
42 var _flushCallback = function () {
43 if (_callback !== null) {
44 try {
45 var currentTime = exports.unstable_now();
46 var hasRemainingTime = true;
47
48 _callback(hasRemainingTime, currentTime);
49
50 _callback = null;
51 } catch (e) {
52 setTimeout(_flushCallback, 0);
53 throw e;
54 }
55 }
56 };
57
58 var initialTime = Date.now();
59
60 exports.unstable_now = function () {
61 return Date.now() - initialTime;
62 };
63
64 requestHostCallback = function (cb) {
65 if (_callback !== null) {
66 // Protect against re-entrancy.
67 setTimeout(requestHostCallback, 0, cb);
68 } else {
69 _callback = cb;
70 setTimeout(_flushCallback, 0);
71 }
72 };
73
74 requestHostTimeout = function (cb, ms) {
75 _timeoutID = setTimeout(cb, ms);
76 };
77
78 cancelHostTimeout = function () {
79 clearTimeout(_timeoutID);
80 };
81
82 shouldYieldToHost = function () {
83 return false;
84 };
85
86 requestPaint = exports.unstable_forceFrameRate = function () {};
87} else {
88 // Capture local references to native APIs, in case a polyfill overrides them.
89 var performance = window.performance;
90 var _Date = window.Date;
91 var _setTimeout = window.setTimeout;
92 var _clearTimeout = window.clearTimeout;
93
94 if (typeof console !== 'undefined') {
95 // TODO: Scheduler no longer requires these methods to be polyfilled. But
96 // maybe we want to continue warning if they don't exist, to preserve the
97 // option to rely on it in the future?
98 var requestAnimationFrame = window.requestAnimationFrame;
99 var cancelAnimationFrame = window.cancelAnimationFrame; // TODO: Remove fb.me link
100
101 if (typeof requestAnimationFrame !== 'function') {
102 console.error("This browser doesn't support requestAnimationFrame. " + 'Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
103 }
104
105 if (typeof cancelAnimationFrame !== 'function') {
106 console.error("This browser doesn't support cancelAnimationFrame. " + 'Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
107 }
108 }
109
110 if (typeof performance === 'object' && typeof performance.now === 'function') {
111 exports.unstable_now = function () {
112 return performance.now();
113 };
114 } else {
115 var _initialTime = _Date.now();
116
117 exports.unstable_now = function () {
118 return _Date.now() - _initialTime;
119 };
120 }
121
122 var isMessageLoopRunning = false;
123 var scheduledHostCallback = null;
124 var taskTimeoutID = -1; // Scheduler periodically yields in case there is other work on the main
125 // thread, like user events. By default, it yields multiple times per frame.
126 // It does not attempt to align with frame boundaries, since most tasks don't
127 // need to be frame aligned; for those that do, use requestAnimationFrame.
128
129 var yieldInterval = 5;
130 var deadline = 0; // TODO: Make this configurable
131 // TODO: Adjust this based on priority?
132
133 var maxYieldInterval = 300;
134 var needsPaint = false;
135
136 if (enableIsInputPending && navigator !== undefined && navigator.scheduling !== undefined && navigator.scheduling.isInputPending !== undefined) {
137 var scheduling = navigator.scheduling;
138
139 shouldYieldToHost = function () {
140 var currentTime = exports.unstable_now();
141
142 if (currentTime >= deadline) {
143 // There's no time left. We may want to yield control of the main
144 // thread, so the browser can perform high priority tasks. The main ones
145 // are painting and user input. If there's a pending paint or a pending
146 // input, then we should yield. But if there's neither, then we can
147 // yield less often while remaining responsive. We'll eventually yield
148 // regardless, since there could be a pending paint that wasn't
149 // accompanied by a call to `requestPaint`, or other main thread tasks
150 // like network events.
151 if (needsPaint || scheduling.isInputPending()) {
152 // There is either a pending paint or a pending input.
153 return true;
154 } // There's no pending input. Only yield if we've reached the max
155 // yield interval.
156
157
158 return currentTime >= maxYieldInterval;
159 } else {
160 // There's still time left in the frame.
161 return false;
162 }
163 };
164
165 requestPaint = function () {
166 needsPaint = true;
167 };
168 } else {
169 // `isInputPending` is not available. Since we have no way of knowing if
170 // there's pending input, always yield at the end of the frame.
171 shouldYieldToHost = function () {
172 return exports.unstable_now() >= deadline;
173 }; // Since we yield every frame regardless, `requestPaint` has no effect.
174
175
176 requestPaint = function () {};
177 }
178
179 exports.unstable_forceFrameRate = function (fps) {
180 if (fps < 0 || fps > 125) {
181 console.error('forceFrameRate takes a positive int between 0 and 125, ' + 'forcing framerates higher than 125 fps is not unsupported');
182 return;
183 }
184
185 if (fps > 0) {
186 yieldInterval = Math.floor(1000 / fps);
187 } else {
188 // reset the framerate
189 yieldInterval = 5;
190 }
191 };
192
193 var performWorkUntilDeadline = function () {
194 if (scheduledHostCallback !== null) {
195 var currentTime = exports.unstable_now(); // Yield after `yieldInterval` ms, regardless of where we are in the vsync
196 // cycle. This means there's always time remaining at the beginning of
197 // the message event.
198
199 deadline = currentTime + yieldInterval;
200 var hasTimeRemaining = true;
201
202 try {
203 var hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
204
205 if (!hasMoreWork) {
206 isMessageLoopRunning = false;
207 scheduledHostCallback = null;
208 } else {
209 // If there's more work, schedule the next message event at the end
210 // of the preceding one.
211 port.postMessage(null);
212 }
213 } catch (error) {
214 // If a scheduler task throws, exit the current browser task so the
215 // error can be observed.
216 port.postMessage(null);
217 throw error;
218 }
219 } else {
220 isMessageLoopRunning = false;
221 } // Yielding to the browser will give it a chance to paint, so we can
222 // reset this.
223
224
225 needsPaint = false;
226 };
227
228 var channel = new MessageChannel();
229 var port = channel.port2;
230 channel.port1.onmessage = performWorkUntilDeadline;
231
232 requestHostCallback = function (callback) {
233 scheduledHostCallback = callback;
234
235 if (!isMessageLoopRunning) {
236 isMessageLoopRunning = true;
237 port.postMessage(null);
238 }
239 };
240
241 requestHostTimeout = function (callback, ms) {
242 taskTimeoutID = _setTimeout(function () {
243 callback(exports.unstable_now());
244 }, ms);
245 };
246
247 cancelHostTimeout = function () {
248 _clearTimeout(taskTimeoutID);
249
250 taskTimeoutID = -1;
251 };
252}
253
254function push(heap, node) {
255 var index = heap.length;
256 heap.push(node);
257 siftUp(heap, node, index);
258}
259function peek(heap) {
260 var first = heap[0];
261 return first === undefined ? null : first;
262}
263function pop(heap) {
264 var first = heap[0];
265
266 if (first !== undefined) {
267 var last = heap.pop();
268
269 if (last !== first) {
270 heap[0] = last;
271 siftDown(heap, last, 0);
272 }
273
274 return first;
275 } else {
276 return null;
277 }
278}
279
280function siftUp(heap, node, i) {
281 var index = i;
282
283 while (true) {
284 var parentIndex = Math.floor((index - 1) / 2);
285 var parent = heap[parentIndex];
286
287 if (parent !== undefined && compare(parent, node) > 0) {
288 // The parent is larger. Swap positions.
289 heap[parentIndex] = node;
290 heap[index] = parent;
291 index = parentIndex;
292 } else {
293 // The parent is smaller. Exit.
294 return;
295 }
296 }
297}
298
299function siftDown(heap, node, i) {
300 var index = i;
301 var length = heap.length;
302
303 while (index < length) {
304 var leftIndex = (index + 1) * 2 - 1;
305 var left = heap[leftIndex];
306 var rightIndex = leftIndex + 1;
307 var right = heap[rightIndex]; // If the left or right node is smaller, swap with the smaller of those.
308
309 if (left !== undefined && compare(left, node) < 0) {
310 if (right !== undefined && compare(right, left) < 0) {
311 heap[index] = right;
312 heap[rightIndex] = node;
313 index = rightIndex;
314 } else {
315 heap[index] = left;
316 heap[leftIndex] = node;
317 index = leftIndex;
318 }
319 } else if (right !== undefined && compare(right, node) < 0) {
320 heap[index] = right;
321 heap[rightIndex] = node;
322 index = rightIndex;
323 } else {
324 // Neither child is smaller. Exit.
325 return;
326 }
327 }
328}
329
330function compare(a, b) {
331 // Compare sort index first, then task id.
332 var diff = a.sortIndex - b.sortIndex;
333 return diff !== 0 ? diff : a.id - b.id;
334}
335
336// TODO: Use symbols?
337var NoPriority = 0;
338var ImmediatePriority = 1;
339var UserBlockingPriority = 2;
340var NormalPriority = 3;
341var LowPriority = 4;
342var IdlePriority = 5;
343
344var runIdCounter = 0;
345var mainThreadIdCounter = 0;
346var profilingStateSize = 4;
347var sharedProfilingBuffer = enableProfiling ? // $FlowFixMe Flow doesn't know about SharedArrayBuffer
348typeof SharedArrayBuffer === 'function' ? new SharedArrayBuffer(profilingStateSize * Int32Array.BYTES_PER_ELEMENT) : // $FlowFixMe Flow doesn't know about ArrayBuffer
349typeof ArrayBuffer === 'function' ? new ArrayBuffer(profilingStateSize * Int32Array.BYTES_PER_ELEMENT) : null // Don't crash the init path on IE9
350: null;
351var profilingState = enableProfiling && sharedProfilingBuffer !== null ? new Int32Array(sharedProfilingBuffer) : []; // We can't read this but it helps save bytes for null checks
352
353var PRIORITY = 0;
354var CURRENT_TASK_ID = 1;
355var CURRENT_RUN_ID = 2;
356var QUEUE_SIZE = 3;
357
358if (enableProfiling) {
359 profilingState[PRIORITY] = NoPriority; // This is maintained with a counter, because the size of the priority queue
360 // array might include canceled tasks.
361
362 profilingState[QUEUE_SIZE] = 0;
363 profilingState[CURRENT_TASK_ID] = 0;
364} // Bytes per element is 4
365
366
367var INITIAL_EVENT_LOG_SIZE = 131072;
368var MAX_EVENT_LOG_SIZE = 524288; // Equivalent to 2 megabytes
369
370var eventLogSize = 0;
371var eventLogBuffer = null;
372var eventLog = null;
373var eventLogIndex = 0;
374var TaskStartEvent = 1;
375var TaskCompleteEvent = 2;
376var TaskErrorEvent = 3;
377var TaskCancelEvent = 4;
378var TaskRunEvent = 5;
379var TaskYieldEvent = 6;
380var SchedulerSuspendEvent = 7;
381var SchedulerResumeEvent = 8;
382
383function logEvent(entries) {
384 if (eventLog !== null) {
385 var offset = eventLogIndex;
386 eventLogIndex += entries.length;
387
388 if (eventLogIndex + 1 > eventLogSize) {
389 eventLogSize *= 2;
390
391 if (eventLogSize > MAX_EVENT_LOG_SIZE) {
392 console.error("Scheduler Profiling: Event log exceeded maximum size. Don't " + 'forget to call `stopLoggingProfilingEvents()`.');
393 stopLoggingProfilingEvents();
394 return;
395 }
396
397 var newEventLog = new Int32Array(eventLogSize * 4);
398 newEventLog.set(eventLog);
399 eventLogBuffer = newEventLog.buffer;
400 eventLog = newEventLog;
401 }
402
403 eventLog.set(entries, offset);
404 }
405}
406
407function startLoggingProfilingEvents() {
408 eventLogSize = INITIAL_EVENT_LOG_SIZE;
409 eventLogBuffer = new ArrayBuffer(eventLogSize * 4);
410 eventLog = new Int32Array(eventLogBuffer);
411 eventLogIndex = 0;
412}
413function stopLoggingProfilingEvents() {
414 var buffer = eventLogBuffer;
415 eventLogSize = 0;
416 eventLogBuffer = null;
417 eventLog = null;
418 eventLogIndex = 0;
419 return buffer;
420}
421function markTaskStart(task, ms) {
422 if (enableProfiling) {
423 profilingState[QUEUE_SIZE]++;
424
425 if (eventLog !== null) {
426 // performance.now returns a float, representing milliseconds. When the
427 // event is logged, it's coerced to an int. Convert to microseconds to
428 // maintain extra degrees of precision.
429 logEvent([TaskStartEvent, ms * 1000, task.id, task.priorityLevel]);
430 }
431 }
432}
433function markTaskCompleted(task, ms) {
434 if (enableProfiling) {
435 profilingState[PRIORITY] = NoPriority;
436 profilingState[CURRENT_TASK_ID] = 0;
437 profilingState[QUEUE_SIZE]--;
438
439 if (eventLog !== null) {
440 logEvent([TaskCompleteEvent, ms * 1000, task.id]);
441 }
442 }
443}
444function markTaskCanceled(task, ms) {
445 if (enableProfiling) {
446 profilingState[QUEUE_SIZE]--;
447
448 if (eventLog !== null) {
449 logEvent([TaskCancelEvent, ms * 1000, task.id]);
450 }
451 }
452}
453function markTaskErrored(task, ms) {
454 if (enableProfiling) {
455 profilingState[PRIORITY] = NoPriority;
456 profilingState[CURRENT_TASK_ID] = 0;
457 profilingState[QUEUE_SIZE]--;
458
459 if (eventLog !== null) {
460 logEvent([TaskErrorEvent, ms * 1000, task.id]);
461 }
462 }
463}
464function markTaskRun(task, ms) {
465 if (enableProfiling) {
466 runIdCounter++;
467 profilingState[PRIORITY] = task.priorityLevel;
468 profilingState[CURRENT_TASK_ID] = task.id;
469 profilingState[CURRENT_RUN_ID] = runIdCounter;
470
471 if (eventLog !== null) {
472 logEvent([TaskRunEvent, ms * 1000, task.id, runIdCounter]);
473 }
474 }
475}
476function markTaskYield(task, ms) {
477 if (enableProfiling) {
478 profilingState[PRIORITY] = NoPriority;
479 profilingState[CURRENT_TASK_ID] = 0;
480 profilingState[CURRENT_RUN_ID] = 0;
481
482 if (eventLog !== null) {
483 logEvent([TaskYieldEvent, ms * 1000, task.id, runIdCounter]);
484 }
485 }
486}
487function markSchedulerSuspended(ms) {
488 if (enableProfiling) {
489 mainThreadIdCounter++;
490
491 if (eventLog !== null) {
492 logEvent([SchedulerSuspendEvent, ms * 1000, mainThreadIdCounter]);
493 }
494 }
495}
496function markSchedulerUnsuspended(ms) {
497 if (enableProfiling) {
498 if (eventLog !== null) {
499 logEvent([SchedulerResumeEvent, ms * 1000, mainThreadIdCounter]);
500 }
501 }
502}
503
504/* eslint-disable no-var */
505// Math.pow(2, 30) - 1
506// 0b111111111111111111111111111111
507
508var maxSigned31BitInt = 1073741823; // Times out immediately
509
510var IMMEDIATE_PRIORITY_TIMEOUT = -1; // Eventually times out
511
512var USER_BLOCKING_PRIORITY = 250;
513var NORMAL_PRIORITY_TIMEOUT = 5000;
514var LOW_PRIORITY_TIMEOUT = 10000; // Never times out
515
516var IDLE_PRIORITY = maxSigned31BitInt; // Tasks are stored on a min heap
517
518var taskQueue = [];
519var timerQueue = []; // Incrementing id counter. Used to maintain insertion order.
520
521var taskIdCounter = 1; // Pausing the scheduler is useful for debugging.
522
523var isSchedulerPaused = false;
524var currentTask = null;
525var currentPriorityLevel = NormalPriority; // This is set while performing work, to prevent re-entrancy.
526
527var isPerformingWork = false;
528var isHostCallbackScheduled = false;
529var isHostTimeoutScheduled = false;
530
531function advanceTimers(currentTime) {
532 // Check for tasks that are no longer delayed and add them to the queue.
533 var timer = peek(timerQueue);
534
535 while (timer !== null) {
536 if (timer.callback === null) {
537 // Timer was cancelled.
538 pop(timerQueue);
539 } else if (timer.startTime <= currentTime) {
540 // Timer fired. Transfer to the task queue.
541 pop(timerQueue);
542 timer.sortIndex = timer.expirationTime;
543 push(taskQueue, timer);
544
545 if (enableProfiling) {
546 markTaskStart(timer, currentTime);
547 timer.isQueued = true;
548 }
549 } else {
550 // Remaining timers are pending.
551 return;
552 }
553
554 timer = peek(timerQueue);
555 }
556}
557
558function handleTimeout(currentTime) {
559 isHostTimeoutScheduled = false;
560 advanceTimers(currentTime);
561
562 if (!isHostCallbackScheduled) {
563 if (peek(taskQueue) !== null) {
564 isHostCallbackScheduled = true;
565 requestHostCallback(flushWork);
566 } else {
567 var firstTimer = peek(timerQueue);
568
569 if (firstTimer !== null) {
570 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
571 }
572 }
573 }
574}
575
576function flushWork(hasTimeRemaining, initialTime) {
577 if (enableProfiling) {
578 markSchedulerUnsuspended(initialTime);
579 } // We'll need a host callback the next time work is scheduled.
580
581
582 isHostCallbackScheduled = false;
583
584 if (isHostTimeoutScheduled) {
585 // We scheduled a timeout but it's no longer needed. Cancel it.
586 isHostTimeoutScheduled = false;
587 cancelHostTimeout();
588 }
589
590 isPerformingWork = true;
591 var previousPriorityLevel = currentPriorityLevel;
592
593 try {
594 if (enableProfiling) {
595 try {
596 return workLoop(hasTimeRemaining, initialTime);
597 } catch (error) {
598 if (currentTask !== null) {
599 var currentTime = exports.unstable_now();
600 markTaskErrored(currentTask, currentTime);
601 currentTask.isQueued = false;
602 }
603
604 throw error;
605 }
606 } else {
607 // No catch in prod codepath.
608 return workLoop(hasTimeRemaining, initialTime);
609 }
610 } finally {
611 currentTask = null;
612 currentPriorityLevel = previousPriorityLevel;
613 isPerformingWork = false;
614
615 if (enableProfiling) {
616 var _currentTime = exports.unstable_now();
617
618 markSchedulerSuspended(_currentTime);
619 }
620 }
621}
622
623function workLoop(hasTimeRemaining, initialTime) {
624 var currentTime = initialTime;
625 advanceTimers(currentTime);
626 currentTask = peek(taskQueue);
627
628 while (currentTask !== null && !(enableSchedulerDebugging && isSchedulerPaused)) {
629 if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost())) {
630 // This currentTask hasn't expired, and we've reached the deadline.
631 break;
632 }
633
634 var callback = currentTask.callback;
635
636 if (callback !== null) {
637 currentTask.callback = null;
638 currentPriorityLevel = currentTask.priorityLevel;
639 var didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
640 markTaskRun(currentTask, currentTime);
641 var continuationCallback = callback(didUserCallbackTimeout);
642 currentTime = exports.unstable_now();
643
644 if (typeof continuationCallback === 'function') {
645 currentTask.callback = continuationCallback;
646 markTaskYield(currentTask, currentTime);
647 } else {
648 if (enableProfiling) {
649 markTaskCompleted(currentTask, currentTime);
650 currentTask.isQueued = false;
651 }
652
653 if (currentTask === peek(taskQueue)) {
654 pop(taskQueue);
655 }
656 }
657
658 advanceTimers(currentTime);
659 } else {
660 pop(taskQueue);
661 }
662
663 currentTask = peek(taskQueue);
664 } // Return whether there's additional work
665
666
667 if (currentTask !== null) {
668 return true;
669 } else {
670 var firstTimer = peek(timerQueue);
671
672 if (firstTimer !== null) {
673 requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
674 }
675
676 return false;
677 }
678}
679
680function unstable_runWithPriority(priorityLevel, eventHandler) {
681 switch (priorityLevel) {
682 case ImmediatePriority:
683 case UserBlockingPriority:
684 case NormalPriority:
685 case LowPriority:
686 case IdlePriority:
687 break;
688
689 default:
690 priorityLevel = NormalPriority;
691 }
692
693 var previousPriorityLevel = currentPriorityLevel;
694 currentPriorityLevel = priorityLevel;
695
696 try {
697 return eventHandler();
698 } finally {
699 currentPriorityLevel = previousPriorityLevel;
700 }
701}
702
703function unstable_next(eventHandler) {
704 var priorityLevel;
705
706 switch (currentPriorityLevel) {
707 case ImmediatePriority:
708 case UserBlockingPriority:
709 case NormalPriority:
710 // Shift down to normal priority
711 priorityLevel = NormalPriority;
712 break;
713
714 default:
715 // Anything lower than normal priority should remain at the current level.
716 priorityLevel = currentPriorityLevel;
717 break;
718 }
719
720 var previousPriorityLevel = currentPriorityLevel;
721 currentPriorityLevel = priorityLevel;
722
723 try {
724 return eventHandler();
725 } finally {
726 currentPriorityLevel = previousPriorityLevel;
727 }
728}
729
730function unstable_wrapCallback(callback) {
731 var parentPriorityLevel = currentPriorityLevel;
732 return function () {
733 // This is a fork of runWithPriority, inlined for performance.
734 var previousPriorityLevel = currentPriorityLevel;
735 currentPriorityLevel = parentPriorityLevel;
736
737 try {
738 return callback.apply(this, arguments);
739 } finally {
740 currentPriorityLevel = previousPriorityLevel;
741 }
742 };
743}
744
745function timeoutForPriorityLevel(priorityLevel) {
746 switch (priorityLevel) {
747 case ImmediatePriority:
748 return IMMEDIATE_PRIORITY_TIMEOUT;
749
750 case UserBlockingPriority:
751 return USER_BLOCKING_PRIORITY;
752
753 case IdlePriority:
754 return IDLE_PRIORITY;
755
756 case LowPriority:
757 return LOW_PRIORITY_TIMEOUT;
758
759 case NormalPriority:
760 default:
761 return NORMAL_PRIORITY_TIMEOUT;
762 }
763}
764
765function unstable_scheduleCallback(priorityLevel, callback, options) {
766 var currentTime = exports.unstable_now();
767 var startTime;
768 var timeout;
769
770 if (typeof options === 'object' && options !== null) {
771 var delay = options.delay;
772
773 if (typeof delay === 'number' && delay > 0) {
774 startTime = currentTime + delay;
775 } else {
776 startTime = currentTime;
777 }
778
779 timeout = typeof options.timeout === 'number' ? options.timeout : timeoutForPriorityLevel(priorityLevel);
780 } else {
781 timeout = timeoutForPriorityLevel(priorityLevel);
782 startTime = currentTime;
783 }
784
785 var expirationTime = startTime + timeout;
786 var newTask = {
787 id: taskIdCounter++,
788 callback: callback,
789 priorityLevel: priorityLevel,
790 startTime: startTime,
791 expirationTime: expirationTime,
792 sortIndex: -1
793 };
794
795 if (enableProfiling) {
796 newTask.isQueued = false;
797 }
798
799 if (startTime > currentTime) {
800 // This is a delayed task.
801 newTask.sortIndex = startTime;
802 push(timerQueue, newTask);
803
804 if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
805 // All tasks are delayed, and this is the task with the earliest delay.
806 if (isHostTimeoutScheduled) {
807 // Cancel an existing timeout.
808 cancelHostTimeout();
809 } else {
810 isHostTimeoutScheduled = true;
811 } // Schedule a timeout.
812
813
814 requestHostTimeout(handleTimeout, startTime - currentTime);
815 }
816 } else {
817 newTask.sortIndex = expirationTime;
818 push(taskQueue, newTask);
819
820 if (enableProfiling) {
821 markTaskStart(newTask, currentTime);
822 newTask.isQueued = true;
823 } // Schedule a host callback, if needed. If we're already performing work,
824 // wait until the next time we yield.
825
826
827 if (!isHostCallbackScheduled && !isPerformingWork) {
828 isHostCallbackScheduled = true;
829 requestHostCallback(flushWork);
830 }
831 }
832
833 return newTask;
834}
835
836function unstable_pauseExecution() {
837 isSchedulerPaused = true;
838}
839
840function unstable_continueExecution() {
841 isSchedulerPaused = false;
842
843 if (!isHostCallbackScheduled && !isPerformingWork) {
844 isHostCallbackScheduled = true;
845 requestHostCallback(flushWork);
846 }
847}
848
849function unstable_getFirstCallbackNode() {
850 return peek(taskQueue);
851}
852
853function unstable_cancelCallback(task) {
854 if (enableProfiling) {
855 if (task.isQueued) {
856 var currentTime = exports.unstable_now();
857 markTaskCanceled(task, currentTime);
858 task.isQueued = false;
859 }
860 } // Null out the callback to indicate the task has been canceled. (Can't
861 // remove from the queue because you can't remove arbitrary nodes from an
862 // array based heap, only the first one.)
863
864
865 task.callback = null;
866}
867
868function unstable_getCurrentPriorityLevel() {
869 return currentPriorityLevel;
870}
871
872function unstable_shouldYield() {
873 var currentTime = exports.unstable_now();
874 advanceTimers(currentTime);
875 var firstTask = peek(taskQueue);
876 return firstTask !== currentTask && currentTask !== null && firstTask !== null && firstTask.callback !== null && firstTask.startTime <= currentTime && firstTask.expirationTime < currentTask.expirationTime || shouldYieldToHost();
877}
878
879var unstable_requestPaint = requestPaint;
880var unstable_Profiling = enableProfiling ? {
881 startLoggingProfilingEvents: startLoggingProfilingEvents,
882 stopLoggingProfilingEvents: stopLoggingProfilingEvents,
883 sharedProfilingBuffer: sharedProfilingBuffer
884} : null;
885
886exports.unstable_ImmediatePriority = ImmediatePriority;
887exports.unstable_UserBlockingPriority = UserBlockingPriority;
888exports.unstable_NormalPriority = NormalPriority;
889exports.unstable_IdlePriority = IdlePriority;
890exports.unstable_LowPriority = LowPriority;
891exports.unstable_runWithPriority = unstable_runWithPriority;
892exports.unstable_next = unstable_next;
893exports.unstable_scheduleCallback = unstable_scheduleCallback;
894exports.unstable_cancelCallback = unstable_cancelCallback;
895exports.unstable_wrapCallback = unstable_wrapCallback;
896exports.unstable_getCurrentPriorityLevel = unstable_getCurrentPriorityLevel;
897exports.unstable_shouldYield = unstable_shouldYield;
898exports.unstable_requestPaint = unstable_requestPaint;
899exports.unstable_continueExecution = unstable_continueExecution;
900exports.unstable_pauseExecution = unstable_pauseExecution;
901exports.unstable_getFirstCallbackNode = unstable_getFirstCallbackNode;
902exports.unstable_Profiling = unstable_Profiling;
903 })();
904}