UNPKG

21.1 kBJavaScriptView Raw
1/** @license React v0.15.0
2 * scheduler-unstable_mock.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;
21
22var currentTime = 0;
23var scheduledCallback = null;
24var scheduledTimeout = null;
25var timeoutTime = -1;
26var yieldedValues = null;
27var expectedNumberOfYields = -1;
28var didStop = false;
29var isFlushing = false;
30var needsPaint = false;
31var shouldYieldForPaint = false;
32
33function requestHostCallback(callback) {
34 scheduledCallback = callback;
35}
36
37
38
39function requestHostTimeout(callback, ms) {
40 scheduledTimeout = callback;
41 timeoutTime = currentTime + ms;
42}
43
44function cancelHostTimeout() {
45 scheduledTimeout = null;
46 timeoutTime = -1;
47}
48
49function shouldYieldToHost() {
50 if (expectedNumberOfYields !== -1 && yieldedValues !== null && yieldedValues.length >= expectedNumberOfYields || shouldYieldForPaint && needsPaint) {
51 // We yielded at least as many values as expected. Stop flushing.
52 didStop = true;
53 return true;
54 }
55 return false;
56}
57
58function getCurrentTime() {
59 return currentTime;
60}
61
62function forceFrameRate() {
63 // No-op
64}
65
66
67
68// Should only be used via an assertion helper that inspects the yielded values.
69function unstable_flushNumberOfYields(count) {
70 if (isFlushing) {
71 throw new Error('Already flushing work.');
72 }
73 if (scheduledCallback !== null) {
74 var cb = scheduledCallback;
75 expectedNumberOfYields = count;
76 isFlushing = true;
77 try {
78 var hasMoreWork = true;
79 do {
80 hasMoreWork = cb(true, currentTime);
81 } while (hasMoreWork && !didStop);
82 if (!hasMoreWork) {
83 scheduledCallback = null;
84 }
85 } finally {
86 expectedNumberOfYields = -1;
87 didStop = false;
88 isFlushing = false;
89 }
90 }
91}
92
93function unstable_flushUntilNextPaint() {
94 if (isFlushing) {
95 throw new Error('Already flushing work.');
96 }
97 if (scheduledCallback !== null) {
98 var cb = scheduledCallback;
99 shouldYieldForPaint = true;
100 needsPaint = false;
101 isFlushing = true;
102 try {
103 var hasMoreWork = true;
104 do {
105 hasMoreWork = cb(true, currentTime);
106 } while (hasMoreWork && !didStop);
107 if (!hasMoreWork) {
108 scheduledCallback = null;
109 }
110 } finally {
111 shouldYieldForPaint = false;
112 didStop = false;
113 isFlushing = false;
114 }
115 }
116}
117
118function unstable_flushExpired() {
119 if (isFlushing) {
120 throw new Error('Already flushing work.');
121 }
122 if (scheduledCallback !== null) {
123 isFlushing = true;
124 try {
125 var hasMoreWork = scheduledCallback(false, currentTime);
126 if (!hasMoreWork) {
127 scheduledCallback = null;
128 }
129 } finally {
130 isFlushing = false;
131 }
132 }
133}
134
135function unstable_flushAllWithoutAsserting() {
136 // Returns false if no work was flushed.
137 if (isFlushing) {
138 throw new Error('Already flushing work.');
139 }
140 if (scheduledCallback !== null) {
141 var cb = scheduledCallback;
142 isFlushing = true;
143 try {
144 var hasMoreWork = true;
145 do {
146 hasMoreWork = cb(true, currentTime);
147 } while (hasMoreWork);
148 if (!hasMoreWork) {
149 scheduledCallback = null;
150 }
151 return true;
152 } finally {
153 isFlushing = false;
154 }
155 } else {
156 return false;
157 }
158}
159
160function unstable_clearYields() {
161 if (yieldedValues === null) {
162 return [];
163 }
164 var values = yieldedValues;
165 yieldedValues = null;
166 return values;
167}
168
169function unstable_flushAll() {
170 if (yieldedValues !== null) {
171 throw new Error('Log is not empty. Assert on the log of yielded values before ' + 'flushing additional work.');
172 }
173 unstable_flushAllWithoutAsserting();
174 if (yieldedValues !== null) {
175 throw new Error('While flushing work, something yielded a value. Use an ' + 'assertion helper to assert on the log of yielded values, e.g. ' + 'expect(Scheduler).toFlushAndYield([...])');
176 }
177}
178
179function unstable_yieldValue(value) {
180 if (yieldedValues === null) {
181 yieldedValues = [value];
182 } else {
183 yieldedValues.push(value);
184 }
185}
186
187function unstable_advanceTime(ms) {
188 currentTime += ms;
189 if (!isFlushing) {
190 if (scheduledTimeout !== null && timeoutTime <= currentTime) {
191 scheduledTimeout(currentTime);
192 timeoutTime = -1;
193 scheduledTimeout = null;
194 }
195 unstable_flushExpired();
196 }
197}
198
199function requestPaint() {
200 needsPaint = true;
201}
202
203/* eslint-disable no-var */
204
205// TODO: Use symbols?
206var ImmediatePriority = 1;
207var UserBlockingPriority = 2;
208var NormalPriority = 3;
209var LowPriority = 4;
210var IdlePriority = 5;
211
212// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
213// Math.pow(2, 30) - 1
214// 0b111111111111111111111111111111
215var maxSigned31BitInt = 1073741823;
216
217// Times out immediately
218var IMMEDIATE_PRIORITY_TIMEOUT = -1;
219// Eventually times out
220var USER_BLOCKING_PRIORITY = 250;
221var NORMAL_PRIORITY_TIMEOUT = 5000;
222var LOW_PRIORITY_TIMEOUT = 10000;
223// Never times out
224var IDLE_PRIORITY = maxSigned31BitInt;
225
226// Tasks are stored as a circular, doubly linked list.
227var firstTask = null;
228var firstDelayedTask = null;
229
230// Pausing the scheduler is useful for debugging.
231var isSchedulerPaused = false;
232
233var currentTask = null;
234var currentPriorityLevel = NormalPriority;
235
236// This is set while performing work, to prevent re-entrancy.
237var isPerformingWork = false;
238
239var isHostCallbackScheduled = false;
240var isHostTimeoutScheduled = false;
241
242function scheduler_flushTaskAtPriority_Immediate(callback, didTimeout) {
243 return callback(didTimeout);
244}
245function scheduler_flushTaskAtPriority_UserBlocking(callback, didTimeout) {
246 return callback(didTimeout);
247}
248function scheduler_flushTaskAtPriority_Normal(callback, didTimeout) {
249 return callback(didTimeout);
250}
251function scheduler_flushTaskAtPriority_Low(callback, didTimeout) {
252 return callback(didTimeout);
253}
254function scheduler_flushTaskAtPriority_Idle(callback, didTimeout) {
255 return callback(didTimeout);
256}
257
258function flushTask(task, currentTime) {
259 // Remove the task from the list before calling the callback. That way the
260 // list is in a consistent state even if the callback throws.
261 var next = task.next;
262 if (next === task) {
263 // This is the only scheduled task. Clear the list.
264 firstTask = null;
265 } else {
266 // Remove the task from its position in the list.
267 if (task === firstTask) {
268 firstTask = next;
269 }
270 var previous = task.previous;
271 previous.next = next;
272 next.previous = previous;
273 }
274 task.next = task.previous = null;
275
276 // Now it's safe to execute the task.
277 var callback = task.callback;
278 var previousPriorityLevel = currentPriorityLevel;
279 var previousTask = currentTask;
280 currentPriorityLevel = task.priorityLevel;
281 currentTask = task;
282 var continuationCallback;
283 try {
284 var didUserCallbackTimeout = task.expirationTime <= currentTime;
285 // Add an extra function to the callstack. Profiling tools can use this
286 // to infer the priority of work that appears higher in the stack.
287 switch (currentPriorityLevel) {
288 case ImmediatePriority:
289 continuationCallback = scheduler_flushTaskAtPriority_Immediate(callback, didUserCallbackTimeout);
290 break;
291 case UserBlockingPriority:
292 continuationCallback = scheduler_flushTaskAtPriority_UserBlocking(callback, didUserCallbackTimeout);
293 break;
294 case NormalPriority:
295 continuationCallback = scheduler_flushTaskAtPriority_Normal(callback, didUserCallbackTimeout);
296 break;
297 case LowPriority:
298 continuationCallback = scheduler_flushTaskAtPriority_Low(callback, didUserCallbackTimeout);
299 break;
300 case IdlePriority:
301 continuationCallback = scheduler_flushTaskAtPriority_Idle(callback, didUserCallbackTimeout);
302 break;
303 }
304 } catch (error) {
305 throw error;
306 } finally {
307 currentPriorityLevel = previousPriorityLevel;
308 currentTask = previousTask;
309 }
310
311 // A callback may return a continuation. The continuation should be scheduled
312 // with the same priority and expiration as the just-finished callback.
313 if (typeof continuationCallback === 'function') {
314 var expirationTime = task.expirationTime;
315 var continuationTask = task;
316 continuationTask.callback = continuationCallback;
317
318 // Insert the new callback into the list, sorted by its timeout. This is
319 // almost the same as the code in `scheduleCallback`, except the callback
320 // is inserted into the list *before* callbacks of equal timeout instead
321 // of after.
322 if (firstTask === null) {
323 // This is the first callback in the list.
324 firstTask = continuationTask.next = continuationTask.previous = continuationTask;
325 } else {
326 var nextAfterContinuation = null;
327 var t = firstTask;
328 do {
329 if (expirationTime <= t.expirationTime) {
330 // This task times out at or after the continuation. We will insert
331 // the continuation *before* this task.
332 nextAfterContinuation = t;
333 break;
334 }
335 t = t.next;
336 } while (t !== firstTask);
337 if (nextAfterContinuation === null) {
338 // No equal or lower priority task was found, which means the new task
339 // is the lowest priority task in the list.
340 nextAfterContinuation = firstTask;
341 } else if (nextAfterContinuation === firstTask) {
342 // The new task is the highest priority task in the list.
343 firstTask = continuationTask;
344 }
345
346 var _previous = nextAfterContinuation.previous;
347 _previous.next = nextAfterContinuation.previous = continuationTask;
348 continuationTask.next = nextAfterContinuation;
349 continuationTask.previous = _previous;
350 }
351 }
352}
353
354function advanceTimers(currentTime) {
355 // Check for tasks that are no longer delayed and add them to the queue.
356 if (firstDelayedTask !== null && firstDelayedTask.startTime <= currentTime) {
357 do {
358 var task = firstDelayedTask;
359 var next = task.next;
360 if (task === next) {
361 firstDelayedTask = null;
362 } else {
363 firstDelayedTask = next;
364 var previous = task.previous;
365 previous.next = next;
366 next.previous = previous;
367 }
368 task.next = task.previous = null;
369 insertScheduledTask(task, task.expirationTime);
370 } while (firstDelayedTask !== null && firstDelayedTask.startTime <= currentTime);
371 }
372}
373
374function handleTimeout(currentTime) {
375 isHostTimeoutScheduled = false;
376 advanceTimers(currentTime);
377
378 if (!isHostCallbackScheduled) {
379 if (firstTask !== null) {
380 isHostCallbackScheduled = true;
381 requestHostCallback(flushWork);
382 } else if (firstDelayedTask !== null) {
383 requestHostTimeout(handleTimeout, firstDelayedTask.startTime - currentTime);
384 }
385 }
386}
387
388function flushWork(hasTimeRemaining, initialTime) {
389 // Exit right away if we're currently paused
390 if (enableSchedulerDebugging && isSchedulerPaused) {
391 return;
392 }
393
394 // We'll need a host callback the next time work is scheduled.
395 isHostCallbackScheduled = false;
396 if (isHostTimeoutScheduled) {
397 // We scheduled a timeout but it's no longer needed. Cancel it.
398 isHostTimeoutScheduled = false;
399 cancelHostTimeout();
400 }
401
402 var currentTime = initialTime;
403 advanceTimers(currentTime);
404
405 isPerformingWork = true;
406 try {
407 if (!hasTimeRemaining) {
408 // Flush all the expired callbacks without yielding.
409 // TODO: Split flushWork into two separate functions instead of using
410 // a boolean argument?
411 while (firstTask !== null && firstTask.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused)) {
412 flushTask(firstTask, currentTime);
413 currentTime = getCurrentTime();
414 advanceTimers(currentTime);
415 }
416 } else {
417 // Keep flushing callbacks until we run out of time in the frame.
418 if (firstTask !== null) {
419 do {
420 flushTask(firstTask, currentTime);
421 currentTime = getCurrentTime();
422 advanceTimers(currentTime);
423 } while (firstTask !== null && !shouldYieldToHost() && !(enableSchedulerDebugging && isSchedulerPaused));
424 }
425 }
426 // Return whether there's additional work
427 if (firstTask !== null) {
428 return true;
429 } else {
430 if (firstDelayedTask !== null) {
431 requestHostTimeout(handleTimeout, firstDelayedTask.startTime - currentTime);
432 }
433 return false;
434 }
435 } finally {
436 isPerformingWork = false;
437 }
438}
439
440function unstable_runWithPriority(priorityLevel, eventHandler) {
441 switch (priorityLevel) {
442 case ImmediatePriority:
443 case UserBlockingPriority:
444 case NormalPriority:
445 case LowPriority:
446 case IdlePriority:
447 break;
448 default:
449 priorityLevel = NormalPriority;
450 }
451
452 var previousPriorityLevel = currentPriorityLevel;
453 currentPriorityLevel = priorityLevel;
454
455 try {
456 return eventHandler();
457 } finally {
458 currentPriorityLevel = previousPriorityLevel;
459 }
460}
461
462function unstable_next(eventHandler) {
463 var priorityLevel;
464 switch (currentPriorityLevel) {
465 case ImmediatePriority:
466 case UserBlockingPriority:
467 case NormalPriority:
468 // Shift down to normal priority
469 priorityLevel = NormalPriority;
470 break;
471 default:
472 // Anything lower than normal priority should remain at the current level.
473 priorityLevel = currentPriorityLevel;
474 break;
475 }
476
477 var previousPriorityLevel = currentPriorityLevel;
478 currentPriorityLevel = priorityLevel;
479
480 try {
481 return eventHandler();
482 } finally {
483 currentPriorityLevel = previousPriorityLevel;
484 }
485}
486
487function unstable_wrapCallback(callback) {
488 var parentPriorityLevel = currentPriorityLevel;
489 return function () {
490 // This is a fork of runWithPriority, inlined for performance.
491 var previousPriorityLevel = currentPriorityLevel;
492 currentPriorityLevel = parentPriorityLevel;
493
494 try {
495 return callback.apply(this, arguments);
496 } finally {
497 currentPriorityLevel = previousPriorityLevel;
498 }
499 };
500}
501
502function timeoutForPriorityLevel(priorityLevel) {
503 switch (priorityLevel) {
504 case ImmediatePriority:
505 return IMMEDIATE_PRIORITY_TIMEOUT;
506 case UserBlockingPriority:
507 return USER_BLOCKING_PRIORITY;
508 case IdlePriority:
509 return IDLE_PRIORITY;
510 case LowPriority:
511 return LOW_PRIORITY_TIMEOUT;
512 case NormalPriority:
513 default:
514 return NORMAL_PRIORITY_TIMEOUT;
515 }
516}
517
518function unstable_scheduleCallback(priorityLevel, callback, options) {
519 var currentTime = getCurrentTime();
520
521 var startTime;
522 var timeout;
523 if (typeof options === 'object' && options !== null) {
524 var delay = options.delay;
525 if (typeof delay === 'number' && delay > 0) {
526 startTime = currentTime + delay;
527 } else {
528 startTime = currentTime;
529 }
530 timeout = typeof options.timeout === 'number' ? options.timeout : timeoutForPriorityLevel(priorityLevel);
531 } else {
532 timeout = timeoutForPriorityLevel(priorityLevel);
533 startTime = currentTime;
534 }
535
536 var expirationTime = startTime + timeout;
537
538 var newTask = {
539 callback: callback,
540 priorityLevel: priorityLevel,
541 startTime: startTime,
542 expirationTime: expirationTime,
543 next: null,
544 previous: null
545 };
546
547 if (startTime > currentTime) {
548 // This is a delayed task.
549 insertDelayedTask(newTask, startTime);
550 if (firstTask === null && firstDelayedTask === newTask) {
551 // All tasks are delayed, and this is the task with the earliest delay.
552 if (isHostTimeoutScheduled) {
553 // Cancel an existing timeout.
554 cancelHostTimeout();
555 } else {
556 isHostTimeoutScheduled = true;
557 }
558 // Schedule a timeout.
559 requestHostTimeout(handleTimeout, startTime - currentTime);
560 }
561 } else {
562 insertScheduledTask(newTask, expirationTime);
563 // Schedule a host callback, if needed. If we're already performing work,
564 // wait until the next time we yield.
565 if (!isHostCallbackScheduled && !isPerformingWork) {
566 isHostCallbackScheduled = true;
567 requestHostCallback(flushWork);
568 }
569 }
570
571 return newTask;
572}
573
574function insertScheduledTask(newTask, expirationTime) {
575 // Insert the new task into the list, ordered first by its timeout, then by
576 // insertion. So the new task is inserted after any other task the
577 // same timeout
578 if (firstTask === null) {
579 // This is the first task in the list.
580 firstTask = newTask.next = newTask.previous = newTask;
581 } else {
582 var next = null;
583 var task = firstTask;
584 do {
585 if (expirationTime < task.expirationTime) {
586 // The new task times out before this one.
587 next = task;
588 break;
589 }
590 task = task.next;
591 } while (task !== firstTask);
592
593 if (next === null) {
594 // No task with a later timeout was found, which means the new task has
595 // the latest timeout in the list.
596 next = firstTask;
597 } else if (next === firstTask) {
598 // The new task has the earliest expiration in the entire list.
599 firstTask = newTask;
600 }
601
602 var previous = next.previous;
603 previous.next = next.previous = newTask;
604 newTask.next = next;
605 newTask.previous = previous;
606 }
607}
608
609function insertDelayedTask(newTask, startTime) {
610 // Insert the new task into the list, ordered by its start time.
611 if (firstDelayedTask === null) {
612 // This is the first task in the list.
613 firstDelayedTask = newTask.next = newTask.previous = newTask;
614 } else {
615 var next = null;
616 var task = firstDelayedTask;
617 do {
618 if (startTime < task.startTime) {
619 // The new task times out before this one.
620 next = task;
621 break;
622 }
623 task = task.next;
624 } while (task !== firstDelayedTask);
625
626 if (next === null) {
627 // No task with a later timeout was found, which means the new task has
628 // the latest timeout in the list.
629 next = firstDelayedTask;
630 } else if (next === firstDelayedTask) {
631 // The new task has the earliest expiration in the entire list.
632 firstDelayedTask = newTask;
633 }
634
635 var previous = next.previous;
636 previous.next = next.previous = newTask;
637 newTask.next = next;
638 newTask.previous = previous;
639 }
640}
641
642function unstable_pauseExecution() {
643 isSchedulerPaused = true;
644}
645
646function unstable_continueExecution() {
647 isSchedulerPaused = false;
648 if (!isHostCallbackScheduled && !isPerformingWork) {
649 isHostCallbackScheduled = true;
650 requestHostCallback(flushWork);
651 }
652}
653
654function unstable_getFirstCallbackNode() {
655 return firstTask;
656}
657
658function unstable_cancelCallback(task) {
659 var next = task.next;
660 if (next === null) {
661 // Already cancelled.
662 return;
663 }
664
665 if (task === next) {
666 if (task === firstTask) {
667 firstTask = null;
668 } else if (task === firstDelayedTask) {
669 firstDelayedTask = null;
670 }
671 } else {
672 if (task === firstTask) {
673 firstTask = next;
674 } else if (task === firstDelayedTask) {
675 firstDelayedTask = next;
676 }
677 var previous = task.previous;
678 previous.next = next;
679 next.previous = previous;
680 }
681
682 task.next = task.previous = null;
683}
684
685function unstable_getCurrentPriorityLevel() {
686 return currentPriorityLevel;
687}
688
689function unstable_shouldYield() {
690 var currentTime = getCurrentTime();
691 advanceTimers(currentTime);
692 return currentTask !== null && firstTask !== null && firstTask.startTime <= currentTime && firstTask.expirationTime < currentTask.expirationTime || shouldYieldToHost();
693}
694
695var unstable_requestPaint = requestPaint;
696
697exports.unstable_flushAllWithoutAsserting = unstable_flushAllWithoutAsserting;
698exports.unstable_flushNumberOfYields = unstable_flushNumberOfYields;
699exports.unstable_flushExpired = unstable_flushExpired;
700exports.unstable_clearYields = unstable_clearYields;
701exports.unstable_flushUntilNextPaint = unstable_flushUntilNextPaint;
702exports.unstable_flushAll = unstable_flushAll;
703exports.unstable_yieldValue = unstable_yieldValue;
704exports.unstable_advanceTime = unstable_advanceTime;
705exports.unstable_ImmediatePriority = ImmediatePriority;
706exports.unstable_UserBlockingPriority = UserBlockingPriority;
707exports.unstable_NormalPriority = NormalPriority;
708exports.unstable_IdlePriority = IdlePriority;
709exports.unstable_LowPriority = LowPriority;
710exports.unstable_runWithPriority = unstable_runWithPriority;
711exports.unstable_next = unstable_next;
712exports.unstable_scheduleCallback = unstable_scheduleCallback;
713exports.unstable_cancelCallback = unstable_cancelCallback;
714exports.unstable_wrapCallback = unstable_wrapCallback;
715exports.unstable_getCurrentPriorityLevel = unstable_getCurrentPriorityLevel;
716exports.unstable_shouldYield = unstable_shouldYield;
717exports.unstable_requestPaint = unstable_requestPaint;
718exports.unstable_continueExecution = unstable_continueExecution;
719exports.unstable_pauseExecution = unstable_pauseExecution;
720exports.unstable_getFirstCallbackNode = unstable_getFirstCallbackNode;
721exports.unstable_now = getCurrentTime;
722exports.unstable_forceFrameRate = forceFrameRate;
723 })();
724}