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', () => {