UNPKG

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