@samitouri / QOS-React-2 / commits / 8ac9600574

Remove priority coalescing and PriorityLevel module (#11187)

Coalescing is the only feature that depends on PriorityLevel. Since we're not sure if coalescing is even valuable, we'll remove it for now. If it turns out we need it, we can add it back later.

Andrew Clark committed Oct 11, 2017 at 14:48 UTC 8ac9600574dc4b4445b32b2d7bcba3c9c0bed6f1
10 files changed +121 -445
src/renderers/shared/fiber/ReactFiber.js
-10
@@ -20,7 +20,6 @@ import type {
20 import type {TypeOfWork} from 'ReactTypeOfWork';
21 import type {TypeOfInternalContext} from 'ReactTypeOfInternalContext';
22 import type {TypeOfSideEffect} from 'ReactTypeOfSideEffect';
23 -import type {PriorityLevel} from 'ReactPriorityLevel';
23 import type {ExpirationTime} from 'ReactFiberExpirationTime';
24 import type {UpdateQueue} from 'ReactFiberUpdateQueue';
25
@@ -36,8 +35,6 @@ var {
35 Fragment,
36 } = require('ReactTypeOfWork');
37
39 -var {NoWork: NoWorkPriority} = require('ReactPriorityLevel');
40 -
38 var {NoWork} = require('ReactFiberExpirationTime');
39
40 var {NoContext} = require('ReactTypeOfInternalContext');
@@ -452,10 +449,3 @@ exports.createFiberFromPortal = function(
449 };
450 return fiber;
451 };
455 -
456 -exports.largerPriority = function(
457 - p1: PriorityLevel,
458 - p2: PriorityLevel,
459 -): PriorityLevel {
460 - return p1 !== NoWorkPriority && (p2 === NoWorkPriority || p2 > p1) ? p1 : p2;
461 -};
src/renderers/shared/fiber/ReactFiberBeginWork.js
+2 -13
@@ -16,7 +16,6 @@ import type {HostContext} from 'ReactFiberHostContext';
16 import type {HydrationContext} from 'ReactFiberHydrationContext';
17 import type {FiberRoot} from 'ReactFiberRoot';
18 import type {HostConfig} from 'ReactFiberReconciler';
19 -import type {PriorityLevel} from 'ReactPriorityLevel';
19 import type {ExpirationTime} from 'ReactFiberExpirationTime';
20
21 var {
@@ -73,15 +72,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
72 hostContext: HostContext<C, CX>,
73 hydrationContext: HydrationContext<C, CX>,
74 scheduleUpdate: (fiber: Fiber, expirationTime: ExpirationTime) => void,
76 - getPriorityContext: (
77 - fiber: Fiber,
78 - forceAsync: boolean,
79 - ) => PriorityLevel | null,
80 - recalculateCurrentTime: () => ExpirationTime,
81 - getExpirationTimeForPriority: (
82 - currentTime: ExpirationTime,
83 - priorityLevel: PriorityLevel | null,
84 - ) => ExpirationTime,
75 + getExpirationTime: (fiber: Fiber, forceAsync: boolean) => ExpirationTime,
76 ) {
77 const {
78 shouldSetTextContent,
@@ -105,11 +96,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
96 updateClassInstance,
97 } = ReactFiberClassComponent(
98 scheduleUpdate,
108 - getPriorityContext,
99 + getExpirationTime,
100 memoizeProps,
101 memoizeState,
111 - recalculateCurrentTime,
112 - getExpirationTimeForPriority,
102 );
103
104 // TODO: Remove this and use reconcileChildrenAtExpirationTime directly.
src/renderers/shared/fiber/ReactFiberClassComponent.js
+7 -51
@@ -11,7 +11,6 @@
11 'use strict';
12
13 import type {Fiber} from 'ReactFiber';
14 -import type {PriorityLevel} from 'ReactPriorityLevel';
14 import type {ExpirationTime} from 'ReactFiberExpirationTime';
15
16 var {Update} = require('ReactTypeOfSideEffect');
@@ -79,84 +78,41 @@ if (__DEV__) {
78
79 module.exports = function(
80 scheduleUpdate: (fiber: Fiber, expirationTime: ExpirationTime) => void,
82 - getPriorityContext: (
83 - fiber: Fiber,
84 - forceAsync: boolean,
85 - ) => PriorityLevel | null,
81 + getExpirationTime: (fiber: Fiber, forceAsync: boolean) => ExpirationTime,
82 memoizeProps: (workInProgress: Fiber, props: any) => void,
83 memoizeState: (workInProgress: Fiber, state: any) => void,
88 - recalculateCurrentTime: () => ExpirationTime,
89 - getExpirationTimeForPriority: (
90 - currentTime: ExpirationTime,
91 - priorityLevel: PriorityLevel | null,
92 - ) => ExpirationTime,
84 ) {
85 // Class component state updater
86 const updater = {
87 isMounted,
88 enqueueSetState(instance, partialState, callback) {
89 const fiber = ReactInstanceMap.get(instance);
99 - const priorityLevel = getPriorityContext(fiber, false);
100 - const currentTime = recalculateCurrentTime();
101 - const expirationTime = getExpirationTimeForPriority(
102 - currentTime,
103 - priorityLevel,
104 - );
90 + const expirationTime = getExpirationTime(fiber, false);
91 callback = callback === undefined ? null : callback;
92 if (__DEV__) {
93 warnOnInvalidCallback(callback, 'setState');
94 }
109 - addUpdate(
110 - fiber,
111 - partialState,
112 - callback,
113 - priorityLevel,
114 - expirationTime,
115 - currentTime,
116 - );
95 + addUpdate(fiber, partialState, callback, expirationTime);
96 scheduleUpdate(fiber, expirationTime);
97 },
98 enqueueReplaceState(instance, state, callback) {
99 const fiber = ReactInstanceMap.get(instance);
121 - const priorityLevel = getPriorityContext(fiber, false);
122 - const currentTime = recalculateCurrentTime();
123 - const expirationTime = getExpirationTimeForPriority(
124 - currentTime,
125 - priorityLevel,
126 - );
100 + const expirationTime = getExpirationTime(fiber, false);
101 callback = callback === undefined ? null : callback;
102 if (__DEV__) {
103 warnOnInvalidCallback(callback, 'replaceState');
104 }
131 - addReplaceUpdate(
132 - fiber,
133 - state,
134 - callback,
135 - priorityLevel,
136 - expirationTime,
137 - currentTime,
138 - );
105 + addReplaceUpdate(fiber, state, callback, expirationTime);
106 scheduleUpdate(fiber, expirationTime);
107 },
108 enqueueForceUpdate(instance, callback) {
109 const fiber = ReactInstanceMap.get(instance);
143 - const priorityLevel = getPriorityContext(fiber, false);
144 - const currentTime = recalculateCurrentTime();
145 - const expirationTime = getExpirationTimeForPriority(
146 - currentTime,
147 - priorityLevel,
148 - );
110 + const expirationTime = getExpirationTime(fiber, false);
111 callback = callback === undefined ? null : callback;
112 if (__DEV__) {
113 warnOnInvalidCallback(callback, 'forceUpdate');
114 }
153 - addForceUpdate(
154 - fiber,
155 - callback,
156 - priorityLevel,
157 - expirationTime,
158 - currentTime,
159 - );
115 + addForceUpdate(fiber, callback, expirationTime);
116 scheduleUpdate(fiber, expirationTime);
117 },
118 };
src/renderers/shared/fiber/ReactFiberExpirationTime.js
+19 -63
@@ -10,18 +10,6 @@
10
11 'use strict';
12
13 -import type {PriorityLevel} from 'ReactPriorityLevel';
14 -const {
15 - NoWork: NoWorkPriority,
16 - SynchronousPriority,
17 - TaskPriority,
18 - HighPriority,
19 - LowPriority,
20 - OffscreenPriority,
21 -} = require('ReactPriorityLevel');
22 -
23 -const invariant = require('fbjs/lib/invariant');
24 -
13 // TODO: Use an opaque type once ESLint et al support the syntax
14 export type ExpirationTime = number;
15
@@ -33,6 +21,8 @@ const Never = 2147483647; // Max int32: Math.pow(2, 31) - 1
21 const UNIT_SIZE = 10;
22 const MAGIC_NUMBER_OFFSET = 3;
23
24 +exports.Sync = Sync;
25 +exports.Task = Task;
26 exports.NoWork = NoWork;
27 exports.Never = Never;
28
@@ -58,66 +48,32 @@ function bucket(
48 );
49 }
50
61 -// Given the current clock time and a priority level, returns an expiration time
62 -// that represents a point in the future by which some work should complete.
63 -// The lower the priority, the further out the expiration time. We use rounding
64 -// to batch like updates together. The further out the expiration time, the
65 -// more we want to batch, so we use a larger precision when rounding.
66 -function priorityToExpirationTime(
67 - currentTime: ExpirationTime,
68 - priorityLevel: PriorityLevel,
69 -): ExpirationTime {
70 - switch (priorityLevel) {
71 - case NoWork:
72 - return NoWorkPriority;
73 - case SynchronousPriority:
74 - return Sync;
75 - case TaskPriority:
76 - return Task;
77 - case HighPriority: {
78 - // Should complete within ~100ms. 120ms max.
79 - return bucket(currentTime, 100, 20);
80 - }
81 - case LowPriority: {
82 - // Should complete within ~1000ms. 1200ms max.
83 - return bucket(currentTime, 1000, 200);
84 - }
85 - case OffscreenPriority:
86 - return Never;
87 - default:
88 - invariant(
89 - false,
90 - 'Switch statement should be exhuastive. ' +
91 - 'This error is likely caused by a bug in React. Please file an issue.',
92 - );
93 - }
51 +// Given the current clock time, returns an expiration time. We use rounding
52 +// to batch like updates together.
53 +function asyncExpirationTime(currentTime: ExpirationTime) {
54 + // Should complete within ~1000ms. 1200ms max.
55 + return bucket(currentTime, 1000, 200);
56 }
95 -exports.priorityToExpirationTime = priorityToExpirationTime;
57 +exports.asyncExpirationTime = asyncExpirationTime;
58
59 // Given the current clock time and an expiration time, returns the
98 -// corresponding priority level. The more time has advanced, the higher the
99 -// priority level.
100 -function expirationTimeToPriorityLevel(
60 +// relative expiration time. Possible values include NoWork, Sync, Task, and
61 +// Never. All other values represent an async expiration time.
62 +function relativeExpirationTime(
63 currentTime: ExpirationTime,
64 expirationTime: ExpirationTime,
103 -): PriorityLevel {
104 - // First check for magic values
65 +): ExpirationTime {
66 switch (expirationTime) {
106 - case NoWorkPriority:
107 - return NoWork;
67 + case NoWork:
68 case Sync:
109 - return SynchronousPriority;
69 case Task:
111 - return TaskPriority;
70 case Never:
113 - return OffscreenPriority;
114 - default:
115 - break;
71 + return expirationTime;
72 }
117 - if (expirationTime <= currentTime) {
118 - return TaskPriority;
73 + const delta = expirationTime - currentTime;
74 + if (delta <= 0) {
75 + return Task;
76 }
120 - // TODO: We don't currently distinguish between high and low priority.
121 - return LowPriority;
77 + return msToExpirationTime(delta);
78 }
123 -exports.expirationTimeToPriorityLevel = expirationTimeToPriorityLevel;
79 +exports.relativeExpirationTime = relativeExpirationTime;
src/renderers/shared/fiber/ReactFiberReconciler.js
+3 -17
@@ -236,9 +236,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
236
237 var {
238 scheduleUpdate,
239 - getPriorityContext,
240 - getExpirationTimeForPriority,
241 - recalculateCurrentTime,
239 + getExpirationTime,
240 batchedUpdates,
241 unbatchedUpdates,
242 flushSync,
@@ -277,12 +275,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
275 element.type != null &&
276 element.type.prototype != null &&
277 (element.type.prototype: any).unstable_isAsyncReactComponent === true;
280 - const priorityLevel = getPriorityContext(current, forceAsync);
281 - const currentTime = recalculateCurrentTime();
282 - const expirationTime = getExpirationTimeForPriority(
283 - currentTime,
284 - priorityLevel,
285 - );
278 + const expirationTime = getExpirationTime(current, forceAsync);
279 const nextState = {element};
280 callback = callback === undefined ? null : callback;
281 if (__DEV__) {
@@ -293,14 +286,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
286 callback,
287 );
288 }
296 - addTopLevelUpdate(
297 - current,
298 - nextState,
299 - callback,
300 - priorityLevel,
301 - expirationTime,
302 - currentTime,
303 - );
289 + addTopLevelUpdate(current, nextState, callback, expirationTime);
290 scheduleUpdate(current, expirationTime);
291 }
292
src/renderers/shared/fiber/ReactFiberScheduler.js
+80 -125
@@ -13,7 +13,6 @@
13 import type {Fiber} from 'ReactFiber';
14 import type {FiberRoot} from 'ReactFiberRoot';
15 import type {HostConfig, Deadline} from 'ReactFiberReconciler';
16 -import type {PriorityLevel} from 'ReactPriorityLevel';
16 import type {HydrationContext} from 'ReactFiberHydrationContext';
17 import type {ExpirationTime} from 'ReactFiberExpirationTime';
18
@@ -56,18 +55,12 @@ var {onCommitRoot} = require('ReactFiberDevToolsHook');
55
56 var {
57 NoWork,
59 - SynchronousPriority,
60 - TaskPriority,
61 - HighPriority,
62 - LowPriority,
63 - OffscreenPriority,
64 -} = require('ReactPriorityLevel');
65 -
66 -var {
58 + Task,
59 + Sync,
60 Never,
61 msToExpirationTime,
69 - priorityToExpirationTime,
70 - expirationTimeToPriorityLevel,
62 + asyncExpirationTime,
63 + relativeExpirationTime,
64 } = require('ReactFiberExpirationTime');
65
66 var {AsyncUpdates} = require('ReactTypeOfInternalContext');
@@ -173,9 +166,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
166 hostContext,
167 hydrationContext,
168 scheduleUpdate,
176 - getPriorityContext,
177 - recalculateCurrentTime,
178 - getExpirationTimeForPriority,
169 + getExpirationTime,
170 );
171 const {completeWork} = ReactFiberCompleteWork(
172 config,
@@ -202,9 +193,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
193 const startTime = now();
194 let mostRecentCurrentTime: ExpirationTime = msToExpirationTime(0);
195
205 - // The priority level to use when scheduling an update. We use NoWork to
206 - // represent the default priority.
207 - let priorityContext: PriorityLevel = NoWork;
196 + // Represents the expiration time that incoming updates should use. (If this
197 + // is NoWork, use the default strategy: async updates in async mode, sync
198 + // updates in sync mode.)
199 + let expirationContext: ExpirationTime = NoWork;
200
201 // Keeps track of whether we're currently in a work loop.
202 let isPerformingWork: boolean = false;
@@ -788,7 +780,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
780 }
781
782 function performDeferredWork(deadline: Deadline) {
791 - performWork(OffscreenPriority, deadline);
783 + performWork(Never, deadline);
784 }
785
786 function handleCommitPhaseErrors() {
@@ -843,21 +835,21 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
835 minExpirationTime: ExpirationTime,
836 deadline: Deadline | null,
837 ) {
846 - if (pendingCommit !== null) {
847 - commitAllWork(pendingCommit);
848 - handleCommitPhaseErrors();
849 - } else if (nextUnitOfWork === null) {
850 - resetNextUnitOfWork();
851 - }
838 + loop: do {
839 + if (pendingCommit !== null) {
840 + commitAllWork(pendingCommit);
841 + handleCommitPhaseErrors();
842 + } else if (nextUnitOfWork === null) {
843 + resetNextUnitOfWork();
844 + }
845
853 - if (
854 - nextRenderExpirationTime === NoWork ||
855 - nextRenderExpirationTime > minExpirationTime
856 - ) {
857 - return;
858 - }
846 + if (
847 + nextRenderExpirationTime === NoWork ||
848 + nextRenderExpirationTime > minExpirationTime
849 + ) {
850 + return;
851 + }
852
860 - loop: do {
853 if (nextRenderExpirationTime <= mostRecentCurrentTime) {
854 // Flush all expired work.
855 while (nextUnitOfWork !== null) {
@@ -926,21 +918,21 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
918 // There might be work left. Depending on the priority, we should
919 // either perform it now or schedule a callback to perform it later.
920 const currentTime = recalculateCurrentTime();
929 - switch (expirationTimeToPriorityLevel(
930 - currentTime,
931 - nextRenderExpirationTime,
932 - )) {
933 - case SynchronousPriority:
934 - case TaskPriority:
921 + switch (relativeExpirationTime(currentTime, nextRenderExpirationTime)) {
922 + case NoWork:
923 + // No work left. We can exit.
924 + break loop;
925 + case Sync:
926 + case Task:
927 // We have remaining synchronous or task work. Keep performing it,
928 // regardless of whether we're inside a callback.
929 if (nextRenderExpirationTime <= minExpirationTime) {
930 + // Sometimes minExpirationTime is Sync, which means we should skip
931 + // task work.
932 continue loop;
933 }
934 break loop;
941 - case HighPriority:
942 - case LowPriority:
943 - case OffscreenPriority:
935 + default:
936 // We have remaining async work.
937 if (deadline === null) {
938 // We're not inside a callback. Exit and perform the work during
@@ -957,15 +949,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
949 }
950 // We've run out of time. Exit.
951 break loop;
960 - case NoWork:
961 - // No work left. We can exit.
962 - break loop;
963 - default:
964 - invariant(
965 - false,
966 - 'Switch statement should be exhuastive. ' +
967 - 'This error is likely caused by a bug in React. Please file an issue.',
968 - );
952 }
953 } while (true);
954 }
@@ -993,7 +976,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
976 }
977
978 function performWork(
996 - minPriorityLevel: PriorityLevel,
979 + minExpirationTime: ExpirationTime,
980 deadline: Deadline | null,
981 ) {
982 if (__DEV__) {
@@ -1007,19 +990,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
990 );
991 isPerformingWork = true;
992
1010 - // Updates that occur during the commit phase should have task priority
1011 - // by default. (Render phase updates are special; getPriorityContext
1012 - // accounts for their behavior.)
1013 - const previousPriorityContext = priorityContext;
1014 - priorityContext = TaskPriority;
1015 -
1016 - // Read the current time from the host environment.
1017 - const currentTime = recalculateCurrentTime();
1018 - const minExpirationTime = getExpirationTimeForPriority(
1019 - currentTime,
1020 - minPriorityLevel,
1021 - );
1022 -
993 nestedUpdateCount = 0;
994
995 let didError = false;
@@ -1119,8 +1089,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1089 isCallbackScheduled = true;
1090 }
1091
1122 - priorityContext = previousPriorityContext;
1123 -
1092 const errorToThrow = firstUncaughtError;
1093
1094 // We're done performing work. Time to clean up.
@@ -1473,23 +1441,19 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1441 const root: FiberRoot = (node.stateNode: any);
1442 scheduleRoot(root, expirationTime);
1443 if (!isPerformingWork) {
1476 - const priorityLevel = expirationTimeToPriorityLevel(
1477 - mostRecentCurrentTime,
1478 - expirationTime,
1479 - );
1480 - switch (priorityLevel) {
1481 - case SynchronousPriority:
1444 + switch (expirationTime) {
1445 + case Sync:
1446 if (isUnbatchingUpdates) {
1447 // We're inside unbatchedUpdates, which is inside either
1448 // batchedUpdates or a lifecycle. We should only flush
1449 // synchronous work, not task work.
1486 - performWork(SynchronousPriority, null);
1450 + performWork(Sync, null);
1451 } else {
1452 // Flush both synchronous and task work.
1489 - performWork(TaskPriority, null);
1453 + performWork(Task, null);
1454 }
1455 break;
1492 - case TaskPriority:
1456 + case Task:
1457 invariant(
1458 isBatchingUpdates,
1459 'Task updates can only be scheduled as a nested update or ' +
@@ -1518,58 +1482,50 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1482 }
1483 }
1484
1521 - function getPriorityContext(
1485 + function getExpirationTime(
1486 fiber: Fiber,
1487 forceAsync: boolean,
1524 - ): PriorityLevel | null {
1525 - if (isPerformingWork && !isCommitting) {
1526 - // Updates during the render phase should expire at the same time as
1527 - // the work that is being rendered. Return null to indicate.
1528 - return null;
1529 - }
1530 -
1531 - let priorityLevel = priorityContext;
1532 - if (priorityLevel === NoWork) {
1488 + ): ExpirationTime {
1489 + let expirationTime;
1490 + if (expirationContext !== NoWork) {
1491 + // An explicit expiration context was set;
1492 + expirationTime = expirationContext;
1493 + } else if (isPerformingWork) {
1494 + if (isCommitting) {
1495 + // Updates that occur during the commit phase should have task priority
1496 + // by default.
1497 + expirationTime = Task;
1498 + } else {
1499 + // Updates during the render phase should expire at the same time as
1500 + // the work that is being rendered.
1501 + expirationTime = nextRenderExpirationTime;
1502 + }
1503 + } else {
1504 + // No explicit expiration context was set, and we're not currently
1505 + // performing work. Calculate a new expiration time.
1506 if (
1534 - !useSyncScheduling ||
1535 - fiber.internalContextTag & AsyncUpdates ||
1536 - forceAsync
1507 + useSyncScheduling &&
1508 + !(fiber.internalContextTag & AsyncUpdates) &&
1509 + !forceAsync
1510 ) {
1538 - priorityLevel = LowPriority;
1511 + // This is a sync update
1512 + expirationTime = Sync;
1513 } else {
1540 - priorityLevel = SynchronousPriority;
1514 + // This is an async update
1515 + const currentTime = recalculateCurrentTime();
1516 + expirationTime = asyncExpirationTime(currentTime);
1517 }
1518 }
1519
1544 - // If we're in a batch, or if we're already performing work, downgrade sync
1545 - // priority to task priority
1546 - if (
1547 - priorityLevel === SynchronousPriority &&
1548 - (isPerformingWork || isBatchingUpdates)
1549 - ) {
1550 - return TaskPriority;
1520 + if (expirationTime === Sync && (isCommitting || isBatchingUpdates)) {
1521 + // If we're in a batch, or in the commit phase, downgrade sync to task
1522 + return Task;
1523 }
1552 - return priorityLevel;
1553 - }
1554 -
1555 - function getExpirationTimeForPriority(
1556 - currentTime: ExpirationTime,
1557 - priorityLevel: PriorityLevel | null,
1558 - ): ExpirationTime {
1559 - if (priorityLevel === null) {
1560 - // A priorityLevel of null indicates that this update should expire at
1561 - // the same time as whatever is currently being rendered.
1562 - return nextRenderExpirationTime;
1563 - }
1564 - return priorityToExpirationTime(currentTime, priorityLevel);
1524 + return expirationTime;
1525 }
1526
1527 function scheduleErrorRecovery(fiber: Fiber) {
1568 - const taskTime = getExpirationTimeForPriority(
1569 - mostRecentCurrentTime,
1570 - TaskPriority,
1571 - );
1572 - scheduleUpdateImpl(fiber, taskTime, true);
1528 + scheduleUpdateImpl(fiber, Task, true);
1529 }
1530
1531 function recalculateCurrentTime(): ExpirationTime {
@@ -1589,7 +1545,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1545 // If we're not already inside a batch, we need to flush any task work
1546 // that was created by the user-provided function.
1547 if (!isPerformingWork && !isBatchingUpdates) {
1592 - performWork(TaskPriority, null);
1548 + performWork(Task, null);
1549 }
1550 }
1551 }
@@ -1610,39 +1566,38 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1566
1567 function flushSync<A>(batch: () => A): A {
1568 const previousIsBatchingUpdates = isBatchingUpdates;
1613 - const previousPriorityContext = priorityContext;
1569 + const previousExpirationContext = expirationContext;
1570 isBatchingUpdates = true;
1615 - priorityContext = SynchronousPriority;
1571 + expirationContext = Sync;
1572 try {
1573 return batch();
1574 } finally {
1575 isBatchingUpdates = previousIsBatchingUpdates;
1620 - priorityContext = previousPriorityContext;
1576 + expirationContext = previousExpirationContext;
1577
1578 invariant(
1579 !isPerformingWork,
1580 'flushSync was called from inside a lifecycle method. It cannot be ' +
1581 'called when React is already rendering.',
1582 );
1627 - performWork(TaskPriority, null);
1583 + performWork(Task, null);
1584 }
1585 }
1586
1587 function deferredUpdates<A>(fn: () => A): A {
1632 - const previousPriorityContext = priorityContext;
1633 - priorityContext = LowPriority;
1588 + const previousExpirationContext = expirationContext;
1589 + const currentTime = recalculateCurrentTime();
1590 + expirationContext = asyncExpirationTime(currentTime);
1591 try {
1592 return fn();
1593 } finally {
1637 - priorityContext = previousPriorityContext;
1594 + expirationContext = previousExpirationContext;
1595 }
1596 }
1597
1598 return {
1599 scheduleUpdate: scheduleUpdate,
1643 - getPriorityContext: getPriorityContext,
1644 - recalculateCurrentTime: recalculateCurrentTime,
1645 - getExpirationTimeForPriority: getExpirationTimeForPriority,
1600 + getExpirationTime: getExpirationTime,
1601 batchedUpdates: batchedUpdates,
1602 unbatchedUpdates: unbatchedUpdates,
1603 flushSync: flushSync,
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+8 -62
@@ -11,7 +11,6 @@
11 'use strict';
12
13 import type {Fiber} from 'ReactFiber';
14 -import type {PriorityLevel} from 'ReactPriorityLevel';
14 import type {ExpirationTime} from 'ReactFiberExpirationTime';
15
16 const {Callback: CallbackEffect} = require('ReactTypeOfSideEffect');
@@ -33,7 +32,6 @@ type PartialState<State, Props> =
32 type Callback = mixed;
33
34 export type Update = {
36 - priorityLevel: PriorityLevel | null,
35 expirationTime: ExpirationTime,
36 partialState: PartialState<any, any>,
37 callback: Callback | null,
@@ -82,7 +80,6 @@ function createUpdateQueue(): UpdateQueue {
80
81 function cloneUpdate(update: Update): Update {
82 return {
85 - priorityLevel: update.priorityLevel,
83 expirationTime: update.expirationTime,
84 partialState: update.partialState,
85 callback: update.callback,
@@ -93,31 +90,14 @@ function cloneUpdate(update: Update): Update {
90 };
91 }
92
96 -const COALESCENCE_THRESHOLD: ExpirationTime = 10;
97 -
93 function insertUpdateIntoQueue(
94 queue: UpdateQueue,
95 update: Update,
96 insertAfter: Update | null,
97 insertBefore: Update | null,
103 - currentTime: ExpirationTime,
98 ) {
99 if (insertAfter !== null) {
100 insertAfter.next = update;
107 - // If we receive multiple updates to the same fiber at the same priority
108 - // level, we coalesce them by assigning the same expiration time, so that
109 - // they all flush at the same time. Because this causes an interruption, it
110 - // could lead to starvation, so we stop coalescing once the time until the
111 - // expiration time reaches a certain threshold.
112 - if (
113 - insertAfter !== null &&
114 - insertAfter.priorityLevel === update.priorityLevel
115 - ) {
116 - const coalescedTime = insertAfter.expirationTime;
117 - if (coalescedTime - currentTime > COALESCENCE_THRESHOLD) {
118 - update.expirationTime = coalescedTime;
119 - }
120 - }
101 } else {
102 // This is the first item in the queue.
103 update.next = queue.first;
@@ -207,11 +187,7 @@ function ensureUpdateQueues(fiber: Fiber) {
187 // we shouldn't make a copy.
188 //
189 // If the update is cloned, it returns the cloned update.
210 -function insertUpdate(
211 - fiber: Fiber,
212 - update: Update,
213 - currentTime: ExpirationTime,
214 -): Update | null {
190 +function insertUpdate(fiber: Fiber, update: Update): Update | null {
191 // We'll have at least one and at most two distinct update queues.
192 ensureUpdateQueues(fiber);
193 const queue1 = _queue1;
@@ -238,13 +214,7 @@ function insertUpdate(
214
215 if (queue2 === null) {
216 // If there's no alternate queue, there's nothing else to do but insert.
241 - insertUpdateIntoQueue(
242 - queue1,
243 - update,
244 - insertAfter1,
245 - insertBefore1,
246 - currentTime,
247 - );
217 + insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1);
218 return null;
219 }
220
@@ -256,13 +226,7 @@ function insertUpdate(
226
227 // Now we can insert into the first queue. This must come after finding both
228 // insertion positions because it mutates the list.
259 - insertUpdateIntoQueue(
260 - queue1,
261 - update,
262 - insertAfter1,
263 - insertBefore1,
264 - currentTime,
265 - );
229 + insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1);
230
231 // See if the insertion positions are equal. Be careful to only compare
232 // non-null values.
@@ -285,13 +249,7 @@ function insertUpdate(
249 // The insertion positions are different, so we need to clone the update and
250 // insert the clone into the alternate queue.
251 const update2 = cloneUpdate(update);
288 - insertUpdateIntoQueue(
289 - queue2,
290 - update2,
291 - insertAfter2,
292 - insertBefore2,
293 - currentTime,
294 - );
252 + insertUpdateIntoQueue(queue2, update2, insertAfter2, insertBefore2);
253 return update2;
254 }
255 }
@@ -300,12 +258,9 @@ function addUpdate(
258 fiber: Fiber,
259 partialState: PartialState<any, any> | null,
260 callback: mixed,
303 - priorityLevel: PriorityLevel | null,
261 expirationTime: ExpirationTime,
305 - currentTime: ExpirationTime,
262 ): void {
263 const update = {
308 - priorityLevel,
264 expirationTime,
265 partialState,
266 callback,
@@ -314,7 +269,7 @@ function addUpdate(
269 isTopLevelUnmount: false,
270 next: null,
271 };
317 - insertUpdate(fiber, update, currentTime);
272 + insertUpdate(fiber, update);
273 }
274 exports.addUpdate = addUpdate;
275
@@ -322,12 +277,9 @@ function addReplaceUpdate(
277 fiber: Fiber,
278 state: any | null,
279 callback: Callback | null,
325 - priorityLevel: PriorityLevel | null,
280 expirationTime: ExpirationTime,
327 - currentTime: ExpirationTime,
281 ): void {
282 const update = {
330 - priorityLevel,
283 expirationTime,
284 partialState: state,
285 callback,
@@ -336,19 +288,16 @@ function addReplaceUpdate(
288 isTopLevelUnmount: false,
289 next: null,
290 };
339 - insertUpdate(fiber, update, currentTime);
291 + insertUpdate(fiber, update);
292 }
293 exports.addReplaceUpdate = addReplaceUpdate;
294
295 function addForceUpdate(
296 fiber: Fiber,
297 callback: Callback | null,
346 - priorityLevel: PriorityLevel | null,
298 expirationTime: ExpirationTime,
348 - currentTime: ExpirationTime,
299 ): void {
300 const update = {
351 - priorityLevel,
301 expirationTime,
302 partialState: null,
303 callback,
@@ -357,7 +306,7 @@ function addForceUpdate(
306 isTopLevelUnmount: false,
307 next: null,
308 };
360 - insertUpdate(fiber, update, currentTime);
309 + insertUpdate(fiber, update);
310 }
311 exports.addForceUpdate = addForceUpdate;
312
@@ -377,14 +326,11 @@ function addTopLevelUpdate(
326 fiber: Fiber,
327 partialState: PartialState<any, any>,
328 callback: Callback | null,
380 - priorityLevel: PriorityLevel | null,
329 expirationTime: ExpirationTime,
382 - currentTime: ExpirationTime,
330 ): void {
331 const isTopLevelUnmount = partialState.element === null;
332
333 const update = {
387 - priorityLevel,
334 expirationTime,
335 partialState,
336 callback,
@@ -393,7 +339,7 @@ function addTopLevelUpdate(
339 isTopLevelUnmount,
340 next: null,
341 };
396 - const update2 = insertUpdate(fiber, update, currentTime);
342 + const update2 = insertUpdate(fiber, update);
343
344 if (isTopLevelUnmount) {
345 // TODO: Redesign the top-level mount/update/unmount API to avoid this
src/renderers/shared/fiber/ReactPriorityLevel.js deleted
-22
@@ -1,22 +0,0 @@
1 -/**
2 - * Copyright (c) 2013-present, Facebook, Inc.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @providesModule ReactPriorityLevel
8 - * @flow
9 - */
10 -
11 -'use strict';
12 -
13 -export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5;
14 -
15 -module.exports = {
16 - NoWork: 0, // No work is pending.
17 - SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects.
18 - TaskPriority: 2, // Completes at the end of the current tick.
19 - HighPriority: 3, // Interaction that needs to complete pretty soon to feel responsive.
20 - LowPriority: 4, // Data fetching, or result from updating stores.
21 - OffscreenPriority: 5, // Won't be visible but do the work in case it becomes visible.
22 -};
src/renderers/shared/fiber/__tests__/ReactExpiration-test.js
-82
@@ -40,86 +40,4 @@ describe('ReactExpiration', () => {
40 ReactNoop.flushExpired();
41 expect(ReactNoop.getChildren()).toEqual([span('done')]);
42 });
43 -
44 - it('coalesces updates to the same component', () => {
45 - const foos = [];
46 - class Foo extends React.Component {
47 - constructor() {
48 - super();
49 - this.state = {step: 0};
50 - foos.push(this);
51 - }
52 - render() {
53 - return <span prop={this.state.step} />;
54 - }
55 - }
56 -
57 - ReactNoop.render([<Foo key="A" />, <Foo key="B" />]);
58 - ReactNoop.flush();
59 - const [a, b] = foos;
60 -
61 - a.setState({step: 1});
62 -
63 - // Advance time by 500ms.
64 - ReactNoop.expire(500);
65 -
66 - // Update A again. This update should coalesce with the previous update.
67 - a.setState({step: 2});
68 - // Update B. This is the first update, so it has nothing to coalesce with.
69 - b.setState({step: 1});
70 -
71 - // Advance time. This should be enough to flush both updates to A, but not
72 - // the update to B. If only the first update to A flushes, but not the
73 - // second, then it wasn't coalesced properly.
74 - ReactNoop.expire(600);
75 - ReactNoop.flushExpired();
76 - expect(ReactNoop.getChildren()).toEqual([span(2), span(0)]);
77 -
78 - // Now expire the update to B.
79 - ReactNoop.expire(500);
80 - ReactNoop.flushExpired();
81 - expect(ReactNoop.getChildren()).toEqual([span(2), span(1)]);
82 - });
83 -
84 - it('stops coalescing after a certain threshold', () => {
85 - let instance;
86 - class Foo extends React.Component {
87 - state = {step: 0};
88 - render() {
89 - instance = this;
90 - return <span prop={this.state.step} />;
91 - }
92 - }
93 -
94 - ReactNoop.render(<Foo />);
95 - ReactNoop.flush();
96 -
97 - instance.setState({step: 1});
98 -
99 - // Advance time by 500 ms.
100 - ReactNoop.expire(500);
101 -
102 - // Update again. This update should coalesce with the previous update.
103 - instance.setState({step: 2});
104 -
105 - // Advance time by 480ms. Not enough to expire the updates.
106 - ReactNoop.expire(480);
107 - ReactNoop.flushExpired();
108 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
109 -
110 - // Update again. This update should NOT be coalesced, because the
111 - // previous updates have almost expired.
112 - instance.setState({step: 3});
113 -
114 - // Advance time. This should expire the first two updates,
115 - // but not the third.
116 - ReactNoop.expire(500);
117 - ReactNoop.flushExpired();
118 - expect(ReactNoop.getChildren()).toEqual([span(2)]);
119 -
120 - // Now expire the remaining update.
121 - ReactNoop.expire(1000);
122 - ReactNoop.flushExpired();
123 - expect(ReactNoop.getChildren()).toEqual([span(3)]);
124 - });
43 });
src/renderers/shared/fiber/__tests__/ReactIncrementalTriangle-test.js
+2
@@ -244,6 +244,8 @@ describe('ReactIncrementalTriangle', () => {
244 simulate(step(1), toggle(0), flush(2), step(2), toggle(0));
245 simulate(step(1), flush(3), toggle(0), step(0));
246 simulate(step(1), flush(3), toggle(18), step(0));
247 + simulate(step(4), flush(52), expire(1476), flush(17), step(0));
248 + simulate(interrupt(), toggle(10), step(2), expire(990), flush(46));
249 });
250
251 it('fuzz tester', () => {