Clean up Scheduler forks (#20915)
* Clean up Scheduler forks * Un-shadow variables * Use timer globals directly, add a test for overrides * Remove more window references * Don't crash for undefined globals + tests * Update lint config globals * Fix test by using async act * Add test fixture * Delete test fixture
Ricky committed
May 17, 2021 at 16:53 UTC
e0f89aa056de95afc4c23909fce3d91fefb7dec7
24 files changed
+119
-1535
.codesandbox/ci.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"packages": ["packages/react", "packages/react-dom", "packages/scheduler"],
3
- "buildCommand": "build --type=NODE react/index,react-dom/index,react-dom/server,react-dom/test-utils,scheduler/index,scheduler/unstable_no_dom,react/jsx-runtime,react/jsx-dev-runtime",
3
+ "buildCommand": "build --type=NODE react/index,react-dom/index,react-dom/server,react-dom/test-utils,scheduler/index,react/jsx-runtime,react/jsx-dev-runtime",
4
"node": "12",
5
"publishDirectory": {
6
"react": "build/node_modules/react",
fixtures/dom/src/__tests__/wrong-act-test.js
+2
-2
@@ -81,12 +81,12 @@ it("doesn't warn when you use the right act + renderer: test", () => {
81
});
82
});
83
84
-it('resets correctly across renderers', () => {
84
+it('resets correctly across renderers', async () => {
85
function Effecty() {
86
React.useEffect(() => {}, []);
87
return null;
88
}
89
- TestUtils.act(() => {
89
+ await TestUtils.act(async () => {
90
TestRenderer.act(() => {});
91
expect(() => {
92
TestRenderer.create(<Effecty />);
packages/react-dom/src/__tests__/ReactTestUtilsActUnmockedScheduler-test.js
+1
-3
@@ -33,9 +33,7 @@ function unmount(dom) {
33
34
beforeEach(() => {
35
jest.resetModules();
36
- jest.mock('scheduler', () =>
37
- require.requireActual('scheduler/unstable_no_dom'),
38
- );
36
+ jest.unmock('scheduler');
37
yields = [];
38
React = require('react');
39
ReactDOM = require('react-dom');
packages/react-dom/src/__tests__/ReactUnmockedSchedulerWarning-test.internal.js
+1
-1
@@ -17,7 +17,7 @@ function App() {
17
18
beforeEach(() => {
19
jest.resetModules();
20
- jest.mock('scheduler', () => require('scheduler/unstable_no_dom'));
20
+ jest.unmock('scheduler');
21
React = require('react');
22
ReactDOM = require('react-dom');
23
ReactFeatureFlags = require('shared/ReactFeatureFlags');
packages/react-dom/src/__tests__/ReactUnmockedSchedulerWarning-test.js
+1
-3
@@ -16,9 +16,7 @@ function App() {
16
17
beforeEach(() => {
18
jest.resetModules();
19
- jest.mock('scheduler', () =>
20
- require.requireActual('scheduler/unstable_no_dom'),
21
- );
19
+ jest.unmock('scheduler');
20
React = require('react');
21
ReactDOM = require('react-dom');
22
});
packages/scheduler/index.js
+1
-1
@@ -7,4 +7,4 @@
7
8
'use strict';
9
10
-export * from './src/forks/SchedulerDOM';
10
+export * from './src/forks/Scheduler';
packages/scheduler/npm/index.js
+1
-3
@@ -1,8 +1,6 @@
1
'use strict';
2
3
-if (typeof window === 'undefined' || typeof MessageChannel !== 'function') {
4
- module.exports = require('./unstable_no_dom');
5
-} else if (process.env.NODE_ENV === 'production') {
3
+if (process.env.NODE_ENV === 'production') {
4
module.exports = require('./cjs/scheduler.production.min.js');
5
} else {
6
module.exports = require('./cjs/scheduler.development.js');
packages/scheduler/npm/unstable_no_dom.js
deleted
-7
@@ -1,7 +0,0 @@
1
-'use strict';
2
-
3
-if (process.env.NODE_ENV === 'production') {
4
- module.exports = require('./cjs/scheduler-unstable_no_dom.production.min.js');
5
-} else {
6
- module.exports = require('./cjs/scheduler-unstable_no_dom.development.js');
7
-}
packages/scheduler/package.json
-1
@@ -26,7 +26,6 @@
26
"build-info.json",
27
"index.js",
28
"unstable_mock.js",
29
- "unstable_no_dom.js",
29
"unstable_post_task.js",
30
"cjs/",
31
"umd/"
packages/scheduler/src/__tests__/Scheduler-test.js
renamed
+6
-13
@@ -32,11 +32,9 @@ let NormalPriority;
32
describe('SchedulerBrowser', () => {
33
beforeEach(() => {
34
jest.resetModules();
35
-
36
- // Un-mock scheduler
37
- jest.mock('scheduler', () => require.requireActual('scheduler'));
38
-
35
runtime = installMockBrowserRuntime();
36
+ jest.unmock('scheduler');
37
+
38
performance = global.performance;
39
Scheduler = require('scheduler');
40
cancelCallback = Scheduler.unstable_cancelCallback;
@@ -68,21 +66,16 @@ describe('SchedulerBrowser', () => {
66
},
67
};
68
71
- const window = {};
72
- global.window = window;
73
-
74
- // TODO: Scheduler no longer requires these methods to be polyfilled. But
75
- // maybe we want to continue warning if they don't exist, to preserve the
76
- // option to rely on it in the future?
77
- window.requestAnimationFrame = window.cancelAnimationFrame = () => {};
69
+ // Delete node provide setImmediate so we fall through to MessageChannel.
70
+ delete global.setImmediate;
71
79
- window.setTimeout = (cb, delay) => {
72
+ global.setTimeout = (cb, delay) => {
73
const id = timerIDCounter++;
74
log(`Set Timer`);
75
// TODO
76
return id;
77
};
85
- window.clearTimeout = id => {
78
+ global.clearTimeout = id => {
79
// TODO
80
};
81
packages/scheduler/src/__tests__/SchedulerPostTask-test.js
-2
@@ -30,8 +30,6 @@ let IdlePriority;
30
describe('SchedulerPostTask', () => {
31
beforeEach(() => {
32
jest.resetModules();
33
-
34
- // Un-mock scheduler
33
jest.mock('scheduler', () =>
34
require.requireActual('scheduler/unstable_post_task'),
35
);
packages/scheduler/src/__tests__/SchedulerPostTaskOnly-test.js
deleted
-275
@@ -1,275 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
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
- * @emails react-core
8
- * @jest-environment node
9
- */
10
-
11
-/* eslint-disable no-for-of-loops/no-for-of-loops */
12
-
13
-'use strict';
14
-
15
-let Scheduler;
16
-let runtime;
17
-let performance;
18
-let cancelCallback;
19
-let scheduleCallback;
20
-let NormalPriority;
21
-
22
-describe('SchedulerPostTaskOnly', () => {
23
- beforeEach(() => {
24
- if (!process.env.IS_BUILD) {
25
- jest.resetModules();
26
-
27
- // Un-mock scheduler
28
- jest.mock('scheduler', () =>
29
- require.requireActual('scheduler/unstable_post_task_only'),
30
- );
31
-
32
- runtime = installMockBrowserRuntime();
33
- performance = window.performance;
34
- Scheduler = require('scheduler');
35
- cancelCallback = Scheduler.unstable_cancelCallback;
36
- scheduleCallback = Scheduler.unstable_scheduleCallback;
37
- NormalPriority = Scheduler.unstable_NormalPriority;
38
- }
39
- });
40
-
41
- afterEach(() => {
42
- if (!process.env.IS_BUILD) {
43
- if (!runtime.isLogEmpty()) {
44
- throw Error('Test exited without clearing log.');
45
- }
46
- }
47
- });
48
-
49
- function installMockBrowserRuntime() {
50
- let hasPendingTask = false;
51
- let timerIDCounter = 0;
52
- let eventLog = [];
53
-
54
- // Mock window functions
55
- const window = {};
56
- global.window = window;
57
-
58
- // TODO: Scheduler no longer requires these methods to be polyfilled. But
59
- // maybe we want to continue warning if they don't exist, to preserve the
60
- // option to rely on it in the future?
61
- window.requestAnimationFrame = window.cancelAnimationFrame = () => {};
62
-
63
- let currentTime = 0;
64
- window.performance = {
65
- now() {
66
- return currentTime;
67
- },
68
- };
69
-
70
- window.setTimeout = (cb, delay) => {
71
- const id = timerIDCounter++;
72
- log(`Set Timer`);
73
- // TODO
74
- return id;
75
- };
76
- window.clearTimeout = id => {
77
- // TODO
78
- };
79
-
80
- // Mock browser scheduler.
81
- const scheduler = {};
82
- global.scheduler = scheduler;
83
-
84
- let nextTask;
85
- scheduler.postTask = function(callback) {
86
- if (hasPendingTask) {
87
- throw Error('Task already scheduled');
88
- }
89
- log('Post Task');
90
- hasPendingTask = true;
91
- nextTask = callback;
92
- };
93
-
94
- function ensureLogIsEmpty() {
95
- if (eventLog.length !== 0) {
96
- throw Error('Log is not empty. Call assertLog before continuing.');
97
- }
98
- }
99
-
100
- function advanceTime(ms) {
101
- currentTime += ms;
102
- }
103
-
104
- function fireNextTask() {
105
- ensureLogIsEmpty();
106
- if (!hasPendingTask) {
107
- throw Error('No task was scheduled');
108
- }
109
- hasPendingTask = false;
110
-
111
- log('Task Event');
112
-
113
- // If there's a continuation, it will call postTask again
114
- // which will set nextTask. That means we need to clear
115
- // nextTask before the invocation, otherwise we would
116
- // delete the continuation task.
117
- const task = nextTask;
118
- nextTask = null;
119
- task();
120
- }
121
-
122
- function log(val) {
123
- eventLog.push(val);
124
- }
125
-
126
- function isLogEmpty() {
127
- return eventLog.length === 0;
128
- }
129
-
130
- function assertLog(expected) {
131
- const actual = eventLog;
132
- eventLog = [];
133
- expect(actual).toEqual(expected);
134
- }
135
-
136
- return {
137
- advanceTime,
138
- fireNextTask,
139
- log,
140
- isLogEmpty,
141
- assertLog,
142
- };
143
- }
144
-
145
- // @gate source
146
- it('task that finishes before deadline', () => {
147
- scheduleCallback(NormalPriority, () => {
148
- runtime.log('Task');
149
- });
150
- runtime.assertLog(['Post Task']);
151
- runtime.fireNextTask();
152
- runtime.assertLog(['Task Event', 'Task']);
153
- });
154
-
155
- // @gate source
156
- it('task with continuation', () => {
157
- scheduleCallback(NormalPriority, () => {
158
- runtime.log('Task');
159
- while (!Scheduler.unstable_shouldYield()) {
160
- runtime.advanceTime(1);
161
- }
162
- runtime.log(`Yield at ${performance.now()}ms`);
163
- return () => {
164
- runtime.log('Continuation');
165
- };
166
- });
167
- runtime.assertLog(['Post Task']);
168
-
169
- runtime.fireNextTask();
170
- runtime.assertLog(['Task Event', 'Task', 'Yield at 5ms', 'Post Task']);
171
-
172
- runtime.fireNextTask();
173
- runtime.assertLog(['Task Event', 'Continuation']);
174
- });
175
-
176
- // @gate source
177
- it('multiple tasks', () => {
178
- scheduleCallback(NormalPriority, () => {
179
- runtime.log('A');
180
- });
181
- scheduleCallback(NormalPriority, () => {
182
- runtime.log('B');
183
- });
184
- runtime.assertLog(['Post Task']);
185
- runtime.fireNextTask();
186
- runtime.assertLog(['Task Event', 'A', 'B']);
187
- });
188
-
189
- // @gate source
190
- it('multiple tasks with a yield in between', () => {
191
- scheduleCallback(NormalPriority, () => {
192
- runtime.log('A');
193
- runtime.advanceTime(4999);
194
- });
195
- scheduleCallback(NormalPriority, () => {
196
- runtime.log('B');
197
- });
198
- runtime.assertLog(['Post Task']);
199
- runtime.fireNextTask();
200
- runtime.assertLog([
201
- 'Task Event',
202
- 'A',
203
- // Ran out of time. Post a continuation event.
204
- 'Post Task',
205
- ]);
206
- runtime.fireNextTask();
207
- runtime.assertLog(['Task Event', 'B']);
208
- });
209
-
210
- // @gate source
211
- it('cancels tasks', () => {
212
- const task = scheduleCallback(NormalPriority, () => {
213
- runtime.log('Task');
214
- });
215
- runtime.assertLog(['Post Task']);
216
- cancelCallback(task);
217
- runtime.assertLog([]);
218
- });
219
-
220
- // @gate source
221
- it('throws when a task errors then continues in a new event', () => {
222
- scheduleCallback(NormalPriority, () => {
223
- runtime.log('Oops!');
224
- throw Error('Oops!');
225
- });
226
- scheduleCallback(NormalPriority, () => {
227
- runtime.log('Yay');
228
- });
229
- runtime.assertLog(['Post Task']);
230
-
231
- expect(() => runtime.fireNextTask()).toThrow('Oops!');
232
- runtime.assertLog(['Task Event', 'Oops!', 'Post Task']);
233
-
234
- runtime.fireNextTask();
235
- runtime.assertLog(['Task Event', 'Yay']);
236
- });
237
-
238
- // @gate source
239
- it('schedule new task after queue has emptied', () => {
240
- scheduleCallback(NormalPriority, () => {
241
- runtime.log('A');
242
- });
243
-
244
- runtime.assertLog(['Post Task']);
245
- runtime.fireNextTask();
246
- runtime.assertLog(['Task Event', 'A']);
247
-
248
- scheduleCallback(NormalPriority, () => {
249
- runtime.log('B');
250
- });
251
- runtime.assertLog(['Post Task']);
252
- runtime.fireNextTask();
253
- runtime.assertLog(['Task Event', 'B']);
254
- });
255
-
256
- // @gate source
257
- it('schedule new task after a cancellation', () => {
258
- const handle = scheduleCallback(NormalPriority, () => {
259
- runtime.log('A');
260
- });
261
-
262
- runtime.assertLog(['Post Task']);
263
- cancelCallback(handle);
264
-
265
- runtime.fireNextTask();
266
- runtime.assertLog(['Task Event']);
267
-
268
- scheduleCallback(NormalPriority, () => {
269
- runtime.log('B');
270
- });
271
- runtime.assertLog(['Post Task']);
272
- runtime.fireNextTask();
273
- runtime.assertLog(['Task Event', 'B']);
274
- });
275
-});
packages/scheduler/src/__tests__/SchedulerSetImmediate-test.js
renamed
+46
-16
@@ -18,6 +18,7 @@ let performance;
18
let cancelCallback;
19
let scheduleCallback;
20
let NormalPriority;
21
+let UserBlockingPriority;
22
23
// The Scheduler implementation uses browser APIs like `MessageChannel` and
24
// `setTimeout` to schedule work on the main thread. Most of our tests treat
@@ -32,16 +33,15 @@ let NormalPriority;
33
describe('SchedulerDOMSetImmediate', () => {
34
beforeEach(() => {
35
jest.resetModules();
35
-
36
- // Un-mock scheduler
37
- jest.mock('scheduler', () => require.requireActual('scheduler'));
38
-
36
runtime = installMockBrowserRuntime();
37
+ jest.unmock('scheduler');
38
+
39
performance = global.performance;
40
Scheduler = require('scheduler');
41
cancelCallback = Scheduler.unstable_cancelCallback;
42
scheduleCallback = Scheduler.unstable_scheduleCallback;
43
NormalPriority = Scheduler.unstable_NormalPriority;
44
+ UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
45
});
46
47
afterEach(() => {
@@ -66,21 +66,12 @@ describe('SchedulerDOMSetImmediate', () => {
66
},
67
};
68
69
- const window = {};
70
- global.window = window;
71
-
72
- // TODO: Scheduler no longer requires these methods to be polyfilled. But
73
- // maybe we want to continue warning if they don't exist, to preserve the
74
- // option to rely on it in the future?
75
- window.requestAnimationFrame = window.cancelAnimationFrame = () => {};
76
-
77
- window.setTimeout = (cb, delay) => {
69
+ global.setTimeout = (cb, delay) => {
70
const id = timerIDCounter++;
71
log(`Set Timer`);
80
- // TODO
72
return id;
73
};
83
- window.clearTimeout = id => {
74
+ global.clearTimeout = id => {
75
// TODO
76
};
77
@@ -97,7 +88,7 @@ describe('SchedulerDOMSetImmediate', () => {
88
};
89
90
let pendingSetImmediateCallback = null;
100
- window.setImmediate = function(cb) {
91
+ global.setImmediate = function(cb) {
92
if (pendingSetImmediateCallback) {
93
throw Error('Message event already scheduled');
94
}
@@ -143,6 +134,19 @@ describe('SchedulerDOMSetImmediate', () => {
134
};
135
}
136
137
+ it('does not use setImmediate override', () => {
138
+ global.setImmediate = () => {
139
+ throw new Error('Should not throw');
140
+ };
141
+
142
+ scheduleCallback(NormalPriority, () => {
143
+ runtime.log('Task');
144
+ });
145
+ runtime.assertLog(['Set Immediate']);
146
+ runtime.fireSetImmediate();
147
+ runtime.assertLog(['setImmediate Callback', 'Task']);
148
+ });
149
+
150
it('task that finishes before deadline', () => {
151
scheduleCallback(NormalPriority, () => {
152
runtime.log('Task');
@@ -189,6 +193,18 @@ describe('SchedulerDOMSetImmediate', () => {
193
runtime.assertLog(['setImmediate Callback', 'A', 'B']);
194
});
195
196
+ it('multiple tasks at different priority', () => {
197
+ scheduleCallback(NormalPriority, () => {
198
+ runtime.log('A');
199
+ });
200
+ scheduleCallback(UserBlockingPriority, () => {
201
+ runtime.log('B');
202
+ });
203
+ runtime.assertLog(['Set Immediate']);
204
+ runtime.fireSetImmediate();
205
+ runtime.assertLog(['setImmediate Callback', 'B', 'A']);
206
+ });
207
+
208
it('multiple tasks with a yield in between', () => {
209
scheduleCallback(NormalPriority, () => {
210
runtime.log('A');
@@ -271,3 +287,17 @@ describe('SchedulerDOMSetImmediate', () => {
287
runtime.assertLog(['setImmediate Callback', 'B']);
288
});
289
});
290
+
291
+it('does not crash if setImmediate is undefined', () => {
292
+ jest.resetModules();
293
+ const originalSetImmediate = global.setImmediate;
294
+ try {
295
+ delete global.setImmediate;
296
+ jest.unmock('scheduler');
297
+ expect(() => {
298
+ require('scheduler');
299
+ }).not.toThrow();
300
+ } finally {
301
+ global.setImmediate = originalSetImmediate;
302
+ }
303
+});
packages/scheduler/src/__tests__/SchedulerSetTimeout-test.js
renamed
+36
-9
@@ -5,6 +5,7 @@
5
* LICENSE file in the root directory of this source tree.
6
*
7
* @emails react-core
8
+ * @jest-environment node
9
*/
10
11
'use strict';
@@ -16,21 +17,16 @@ let UserBlockingPriority;
17
let NormalPriority;
18
19
describe('SchedulerNoDOM', () => {
19
- // If Scheduler runs in a non-DOM environment, it falls back to a naive
20
- // implementation using setTimeout. This only meant to be used for testing
21
- // purposes, like with jest's fake timer API.
20
+ // Scheduler falls back to a naive implementation using setTimeout.
21
+ // This is only meant to be used for testing purposes, like with jest's fake timer API.
22
beforeEach(() => {
23
jest.resetModules();
24
jest.useFakeTimers();
25
-
26
- // Un-mock scheduler
27
- jest.mock('scheduler', () =>
28
- require.requireActual('scheduler/unstable_no_dom'),
29
- );
25
+ delete global.setImmediate;
26
+ jest.unmock('scheduler');
27
28
Scheduler = require('scheduler');
29
scheduleCallback = Scheduler.unstable_scheduleCallback;
33
- ImmediatePriority = Scheduler.unstable_ImmediatePriority;
30
UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
31
NormalPriority = Scheduler.unstable_NormalPriority;
32
});
@@ -99,3 +95,34 @@ describe('SchedulerNoDOM', () => {
95
expect(log).toEqual(['B', 'C']);
96
});
97
});
98
+
99
+// See: https://github.com/facebook/react/pull/13088
100
+describe('does not crash non-node SSR environments', () => {
101
+ it('if setTimeout is undefined', () => {
102
+ jest.resetModules();
103
+ const originalSetTimeout = global.setTimeout;
104
+ try {
105
+ delete global.setTimeout;
106
+ jest.unmock('scheduler');
107
+ expect(() => {
108
+ require('scheduler');
109
+ }).not.toThrow();
110
+ } finally {
111
+ global.setTimeout = originalSetTimeout;
112
+ }
113
+ });
114
+
115
+ it('if clearTimeout is undefined', () => {
116
+ jest.resetModules();
117
+ const originalClearTimeout = global.clearTimeout;
118
+ try {
119
+ delete global.clearTimeout;
120
+ jest.unmock('scheduler');
121
+ expect(() => {
122
+ require('scheduler');
123
+ }).not.toThrow();
124
+ } finally {
125
+ global.clearTimeout = originalClearTimeout;
126
+ }
127
+ });
128
+});
packages/scheduler/src/__tests__/SchedulerUMDBundle-test.internal.js
+1
-8
@@ -21,19 +21,12 @@ describe('Scheduling UMD bundle', () => {
21
global.__UMD__ = true;
22
23
jest.resetModules();
24
+ jest.unmock('scheduler');
25
25
- jest.mock('scheduler', () => require.requireActual('scheduler'));
26
-
27
- // Mock a browser environment since we're testing UMD modules.
28
- global.window = {
29
- requestAnimationFrame: jest.fn(),
30
- cancelAnimationFrame: jest.fn(),
31
- };
26
global.MessageChannel = MockMessageChannel;
27
});
28
29
afterEach(() => {
36
- global.window = undefined;
30
global.MessageChannel = undefined;
31
});
32
packages/scheduler/src/forks/Scheduler.js
renamed
+17
-33
@@ -85,34 +85,11 @@ var isHostCallbackScheduled = false;
85
var isHostTimeoutScheduled = false;
86
87
// Capture local references to native APIs, in case a polyfill overrides them.
88
-const setTimeout = window.setTimeout;
89
-const clearTimeout = window.clearTimeout;
90
-const setImmediate = window.setImmediate; // IE and Node.js + jsdom
91
-
92
-if (typeof console !== 'undefined') {
93
- // TODO: Scheduler no longer requires these methods to be polyfilled. But
94
- // maybe we want to continue warning if they don't exist, to preserve the
95
- // option to rely on it in the future?
96
- const requestAnimationFrame = window.requestAnimationFrame;
97
- const cancelAnimationFrame = window.cancelAnimationFrame;
98
-
99
- if (typeof requestAnimationFrame !== 'function') {
100
- // Using console['error'] to evade Babel and ESLint
101
- console['error'](
102
- "This browser doesn't support requestAnimationFrame. " +
103
- 'Make sure that you load a ' +
104
- 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
105
- );
106
- }
107
- if (typeof cancelAnimationFrame !== 'function') {
108
- // Using console['error'] to evade Babel and ESLint
109
- console['error'](
110
- "This browser doesn't support cancelAnimationFrame. " +
111
- 'Make sure that you load a ' +
112
- 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
113
- );
114
- }
115
-}
88
+const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null;
89
+const localClearTimeout =
90
+ typeof clearTimeout === 'function' ? clearTimeout : null;
91
+const localSetImmediate =
92
+ typeof setImmediate !== 'undefined' ? setImmediate : null; // IE and Node.js + jsdom
93
94
function advanceTimers(currentTime) {
95
// Check for tasks that are no longer delayed and add them to the queue.
@@ -552,7 +529,7 @@ const performWorkUntilDeadline = () => {
529
};
530
531
let schedulePerformWorkUntilDeadline;
555
-if (typeof setImmediate === 'function') {
532
+if (typeof localSetImmediate === 'function') {
533
// Node.js and old IE.
534
// There's a few reasons for why we prefer setImmediate.
535
//
@@ -565,15 +542,22 @@ if (typeof setImmediate === 'function') {
542
// If other browsers ever implement it, it's better to use it.
543
// Although both of these would be inferior to native scheduling.
544
schedulePerformWorkUntilDeadline = () => {
568
- setImmediate(performWorkUntilDeadline);
545
+ localSetImmediate(performWorkUntilDeadline);
546
};
570
-} else {
547
+} else if (typeof MessageChannel !== 'undefined') {
548
+ // DOM and Worker environments.
549
+ // We prefer MessageChannel because of the 4ms setTimeout clamping.
550
const channel = new MessageChannel();
551
const port = channel.port2;
552
channel.port1.onmessage = performWorkUntilDeadline;
553
schedulePerformWorkUntilDeadline = () => {
554
port.postMessage(null);
555
};
556
+} else {
557
+ // We should only fallback here in non-browser environments.
558
+ schedulePerformWorkUntilDeadline = () => {
559
+ localSetTimeout(performWorkUntilDeadline, 0);
560
+ };
561
}
562
563
function requestHostCallback(callback) {
@@ -585,13 +569,13 @@ function requestHostCallback(callback) {
569
}
570
571
function requestHostTimeout(callback, ms) {
588
- taskTimeoutID = setTimeout(() => {
572
+ taskTimeoutID = localSetTimeout(() => {
573
callback(getCurrentTime());
574
}, ms);
575
}
576
577
function cancelHostTimeout() {
594
- clearTimeout(taskTimeoutID);
578
+ localClearTimeout(taskTimeoutID);
579
taskTimeoutID = -1;
580
}
581
packages/scheduler/src/forks/SchedulerNoDOM.js
deleted
-473
@@ -1,473 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
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
- */
8
-
9
-/* eslint-disable no-var */
10
-
11
-import {
12
- enableSchedulerDebugging,
13
- enableProfiling,
14
-} from '../SchedulerFeatureFlags';
15
-import {push, pop, peek} from '../SchedulerMinHeap';
16
-
17
-// TODO: Use symbols?
18
-import {
19
- ImmediatePriority,
20
- UserBlockingPriority,
21
- NormalPriority,
22
- LowPriority,
23
- IdlePriority,
24
-} from '../SchedulerPriorities';
25
-import {
26
- markTaskRun,
27
- markTaskYield,
28
- markTaskCompleted,
29
- markTaskCanceled,
30
- markTaskErrored,
31
- markSchedulerSuspended,
32
- markSchedulerUnsuspended,
33
- markTaskStart,
34
- stopLoggingProfilingEvents,
35
- startLoggingProfilingEvents,
36
-} from '../SchedulerProfiling';
37
-
38
-// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
39
-// Math.pow(2, 30) - 1
40
-// 0b111111111111111111111111111111
41
-var maxSigned31BitInt = 1073741823;
42
-
43
-// Times out immediately
44
-var IMMEDIATE_PRIORITY_TIMEOUT = -1;
45
-// Eventually times out
46
-var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
47
-var NORMAL_PRIORITY_TIMEOUT = 5000;
48
-var LOW_PRIORITY_TIMEOUT = 10000;
49
-// Never times out
50
-var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
51
-
52
-// Tasks are stored on a min heap
53
-var taskQueue = [];
54
-var timerQueue = [];
55
-
56
-// Incrementing id counter. Used to maintain insertion order.
57
-var taskIdCounter = 1;
58
-
59
-// Pausing the scheduler is useful for debugging.
60
-var isSchedulerPaused = false;
61
-
62
-var currentTask = null;
63
-var currentPriorityLevel = NormalPriority;
64
-
65
-// This is set while performing work, to prevent re-entrancy.
66
-var isPerformingWork = false;
67
-
68
-var isHostCallbackScheduled = false;
69
-var isHostTimeoutScheduled = false;
70
-
71
-let getCurrentTime;
72
-const hasPerformanceNow =
73
- typeof performance === 'object' && typeof performance.now === 'function';
74
-
75
-if (hasPerformanceNow) {
76
- const localPerformance = performance;
77
- getCurrentTime = () => localPerformance.now();
78
-} else {
79
- const localDate = Date;
80
- const initialTime = localDate.now();
81
- getCurrentTime = () => localDate.now() - initialTime;
82
-}
83
-
84
-function advanceTimers(currentTime) {
85
- // Check for tasks that are no longer delayed and add them to the queue.
86
- let timer = peek(timerQueue);
87
- while (timer !== null) {
88
- if (timer.callback === null) {
89
- // Timer was cancelled.
90
- pop(timerQueue);
91
- } else if (timer.startTime <= currentTime) {
92
- // Timer fired. Transfer to the task queue.
93
- pop(timerQueue);
94
- timer.sortIndex = timer.expirationTime;
95
- push(taskQueue, timer);
96
- if (enableProfiling) {
97
- markTaskStart(timer, currentTime);
98
- timer.isQueued = true;
99
- }
100
- } else {
101
- // Remaining timers are pending.
102
- return;
103
- }
104
- timer = peek(timerQueue);
105
- }
106
-}
107
-
108
-function handleTimeout(currentTime) {
109
- isHostTimeoutScheduled = false;
110
- advanceTimers(currentTime);
111
-
112
- if (!isHostCallbackScheduled) {
113
- if (peek(taskQueue) !== null) {
114
- isHostCallbackScheduled = true;
115
- requestHostCallback(flushWork);
116
- } else {
117
- const firstTimer = peek(timerQueue);
118
- if (firstTimer !== null) {
119
- requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
120
- }
121
- }
122
- }
123
-}
124
-
125
-function flushWork(hasTimeRemaining, initialTime) {
126
- if (enableProfiling) {
127
- markSchedulerUnsuspended(initialTime);
128
- }
129
-
130
- // We'll need a host callback the next time work is scheduled.
131
- isHostCallbackScheduled = false;
132
- if (isHostTimeoutScheduled) {
133
- // We scheduled a timeout but it's no longer needed. Cancel it.
134
- isHostTimeoutScheduled = false;
135
- cancelHostTimeout();
136
- }
137
-
138
- isPerformingWork = true;
139
- const previousPriorityLevel = currentPriorityLevel;
140
- try {
141
- if (enableProfiling) {
142
- try {
143
- return workLoop(hasTimeRemaining, initialTime);
144
- } catch (error) {
145
- if (currentTask !== null) {
146
- const currentTime = getCurrentTime();
147
- markTaskErrored(currentTask, currentTime);
148
- currentTask.isQueued = false;
149
- }
150
- throw error;
151
- }
152
- } else {
153
- // No catch in prod code path.
154
- return workLoop(hasTimeRemaining, initialTime);
155
- }
156
- } finally {
157
- currentTask = null;
158
- currentPriorityLevel = previousPriorityLevel;
159
- isPerformingWork = false;
160
- if (enableProfiling) {
161
- const currentTime = getCurrentTime();
162
- markSchedulerSuspended(currentTime);
163
- }
164
- }
165
-}
166
-
167
-function workLoop(hasTimeRemaining, initialTime) {
168
- let currentTime = initialTime;
169
- advanceTimers(currentTime);
170
- currentTask = peek(taskQueue);
171
- while (
172
- currentTask !== null &&
173
- !(enableSchedulerDebugging && isSchedulerPaused)
174
- ) {
175
- if (
176
- currentTask.expirationTime > currentTime &&
177
- (!hasTimeRemaining || shouldYieldToHost())
178
- ) {
179
- // This currentTask hasn't expired, and we've reached the deadline.
180
- break;
181
- }
182
- const callback = currentTask.callback;
183
- if (typeof callback === 'function') {
184
- currentTask.callback = null;
185
- currentPriorityLevel = currentTask.priorityLevel;
186
- const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
187
- if (enableProfiling) {
188
- markTaskRun(currentTask, currentTime);
189
- }
190
- const continuationCallback = callback(didUserCallbackTimeout);
191
- currentTime = getCurrentTime();
192
- if (typeof continuationCallback === 'function') {
193
- currentTask.callback = continuationCallback;
194
- if (enableProfiling) {
195
- markTaskYield(currentTask, currentTime);
196
- }
197
- } else {
198
- if (enableProfiling) {
199
- markTaskCompleted(currentTask, currentTime);
200
- currentTask.isQueued = false;
201
- }
202
- if (currentTask === peek(taskQueue)) {
203
- pop(taskQueue);
204
- }
205
- }
206
- advanceTimers(currentTime);
207
- } else {
208
- pop(taskQueue);
209
- }
210
- currentTask = peek(taskQueue);
211
- }
212
- // Return whether there's additional work
213
- if (currentTask !== null) {
214
- return true;
215
- } else {
216
- const firstTimer = peek(timerQueue);
217
- if (firstTimer !== null) {
218
- requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
219
- }
220
- return false;
221
- }
222
-}
223
-
224
-function unstable_runWithPriority(priorityLevel, eventHandler) {
225
- switch (priorityLevel) {
226
- case ImmediatePriority:
227
- case UserBlockingPriority:
228
- case NormalPriority:
229
- case LowPriority:
230
- case IdlePriority:
231
- break;
232
- default:
233
- priorityLevel = NormalPriority;
234
- }
235
-
236
- var previousPriorityLevel = currentPriorityLevel;
237
- currentPriorityLevel = priorityLevel;
238
-
239
- try {
240
- return eventHandler();
241
- } finally {
242
- currentPriorityLevel = previousPriorityLevel;
243
- }
244
-}
245
-
246
-function unstable_next(eventHandler) {
247
- var priorityLevel;
248
- switch (currentPriorityLevel) {
249
- case ImmediatePriority:
250
- case UserBlockingPriority:
251
- case NormalPriority:
252
- // Shift down to normal priority
253
- priorityLevel = NormalPriority;
254
- break;
255
- default:
256
- // Anything lower than normal priority should remain at the current level.
257
- priorityLevel = currentPriorityLevel;
258
- break;
259
- }
260
-
261
- var previousPriorityLevel = currentPriorityLevel;
262
- currentPriorityLevel = priorityLevel;
263
-
264
- try {
265
- return eventHandler();
266
- } finally {
267
- currentPriorityLevel = previousPriorityLevel;
268
- }
269
-}
270
-
271
-function unstable_wrapCallback(callback) {
272
- var parentPriorityLevel = currentPriorityLevel;
273
- return function() {
274
- // This is a fork of runWithPriority, inlined for performance.
275
- var previousPriorityLevel = currentPriorityLevel;
276
- currentPriorityLevel = parentPriorityLevel;
277
-
278
- try {
279
- return callback.apply(this, arguments);
280
- } finally {
281
- currentPriorityLevel = previousPriorityLevel;
282
- }
283
- };
284
-}
285
-
286
-function unstable_scheduleCallback(priorityLevel, callback, options) {
287
- var currentTime = getCurrentTime();
288
-
289
- var startTime;
290
- if (typeof options === 'object' && options !== null) {
291
- var delay = options.delay;
292
- if (typeof delay === 'number' && delay > 0) {
293
- startTime = currentTime + delay;
294
- } else {
295
- startTime = currentTime;
296
- }
297
- } else {
298
- startTime = currentTime;
299
- }
300
-
301
- var timeout;
302
- switch (priorityLevel) {
303
- case ImmediatePriority:
304
- timeout = IMMEDIATE_PRIORITY_TIMEOUT;
305
- break;
306
- case UserBlockingPriority:
307
- timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
308
- break;
309
- case IdlePriority:
310
- timeout = IDLE_PRIORITY_TIMEOUT;
311
- break;
312
- case LowPriority:
313
- timeout = LOW_PRIORITY_TIMEOUT;
314
- break;
315
- case NormalPriority:
316
- default:
317
- timeout = NORMAL_PRIORITY_TIMEOUT;
318
- break;
319
- }
320
-
321
- var expirationTime = startTime + timeout;
322
-
323
- var newTask = {
324
- id: taskIdCounter++,
325
- callback,
326
- priorityLevel,
327
- startTime,
328
- expirationTime,
329
- sortIndex: -1,
330
- };
331
- if (enableProfiling) {
332
- newTask.isQueued = false;
333
- }
334
-
335
- if (startTime > currentTime) {
336
- // This is a delayed task.
337
- newTask.sortIndex = startTime;
338
- push(timerQueue, newTask);
339
- if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
340
- // All tasks are delayed, and this is the task with the earliest delay.
341
- if (isHostTimeoutScheduled) {
342
- // Cancel an existing timeout.
343
- cancelHostTimeout();
344
- } else {
345
- isHostTimeoutScheduled = true;
346
- }
347
- // Schedule a timeout.
348
- requestHostTimeout(handleTimeout, startTime - currentTime);
349
- }
350
- } else {
351
- newTask.sortIndex = expirationTime;
352
- push(taskQueue, newTask);
353
- if (enableProfiling) {
354
- markTaskStart(newTask, currentTime);
355
- newTask.isQueued = true;
356
- }
357
- // Schedule a host callback, if needed. If we're already performing work,
358
- // wait until the next time we yield.
359
- if (!isHostCallbackScheduled && !isPerformingWork) {
360
- isHostCallbackScheduled = true;
361
- requestHostCallback(flushWork);
362
- }
363
- }
364
-
365
- return newTask;
366
-}
367
-
368
-function unstable_pauseExecution() {
369
- isSchedulerPaused = true;
370
-}
371
-
372
-function unstable_continueExecution() {
373
- isSchedulerPaused = false;
374
- if (!isHostCallbackScheduled && !isPerformingWork) {
375
- isHostCallbackScheduled = true;
376
- requestHostCallback(flushWork);
377
- }
378
-}
379
-
380
-function unstable_getFirstCallbackNode() {
381
- return peek(taskQueue);
382
-}
383
-
384
-function unstable_cancelCallback(task) {
385
- if (enableProfiling) {
386
- if (task.isQueued) {
387
- const currentTime = getCurrentTime();
388
- markTaskCanceled(task, currentTime);
389
- task.isQueued = false;
390
- }
391
- }
392
-
393
- // Null out the callback to indicate the task has been canceled. (Can't
394
- // remove from the queue because you can't remove arbitrary nodes from an
395
- // array based heap, only the first one.)
396
- task.callback = null;
397
-}
398
-
399
-function unstable_getCurrentPriorityLevel() {
400
- return currentPriorityLevel;
401
-}
402
-
403
-// If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
404
-// fallback to a naive implementation.
405
-let _callback = null;
406
-let _timeoutID = null;
407
-const _flushCallback = function() {
408
- if (_callback !== null) {
409
- try {
410
- const currentTime = getCurrentTime();
411
- const hasRemainingTime = true;
412
- _callback(hasRemainingTime, currentTime);
413
- _callback = null;
414
- } catch (e) {
415
- setTimeout(_flushCallback, 0);
416
- throw e;
417
- }
418
- }
419
-};
420
-
421
-function requestHostCallback(cb) {
422
- if (_callback !== null) {
423
- // Protect against re-entrancy.
424
- setTimeout(requestHostCallback, 0, cb);
425
- } else {
426
- _callback = cb;
427
- setTimeout(_flushCallback, 0);
428
- }
429
-}
430
-
431
-function requestHostTimeout(cb, ms) {
432
- _timeoutID = setTimeout(cb, ms);
433
-}
434
-
435
-function cancelHostTimeout() {
436
- clearTimeout(_timeoutID);
437
-}
438
-
439
-function shouldYieldToHost() {
440
- return false;
441
-}
442
-
443
-function forceFrameRate() {}
444
-
445
-function requestPaint() {}
446
-
447
-export {
448
- ImmediatePriority as unstable_ImmediatePriority,
449
- UserBlockingPriority as unstable_UserBlockingPriority,
450
- NormalPriority as unstable_NormalPriority,
451
- IdlePriority as unstable_IdlePriority,
452
- LowPriority as unstable_LowPriority,
453
- unstable_runWithPriority,
454
- unstable_next,
455
- unstable_scheduleCallback,
456
- unstable_cancelCallback,
457
- unstable_wrapCallback,
458
- unstable_getCurrentPriorityLevel,
459
- shouldYieldToHost as unstable_shouldYield,
460
- requestPaint as unstable_requestPaint,
461
- unstable_continueExecution,
462
- unstable_pauseExecution,
463
- unstable_getFirstCallbackNode,
464
- getCurrentTime as unstable_now,
465
- forceFrameRate as unstable_forceFrameRate,
466
-};
467
-
468
-export const unstable_Profiling = enableProfiling
469
- ? {
470
- startLoggingProfilingEvents,
471
- stopLoggingProfilingEvents,
472
- }
473
- : null;
packages/scheduler/src/forks/SchedulerPostTaskOnly.js
deleted
-598
@@ -1,598 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
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
- */
8
-
9
-/* eslint-disable no-var */
10
-
11
-import {
12
- enableSchedulerDebugging,
13
- enableProfiling,
14
-} from '../SchedulerFeatureFlags';
15
-
16
-import {push, pop, peek} from '../SchedulerMinHeap';
17
-
18
-// TODO: Use symbols?
19
-import {
20
- ImmediatePriority,
21
- UserBlockingPriority,
22
- NormalPriority,
23
- LowPriority,
24
- IdlePriority,
25
-} from '../SchedulerPriorities';
26
-import {
27
- markTaskRun,
28
- markTaskYield,
29
- markTaskCompleted,
30
- markTaskCanceled,
31
- markTaskErrored,
32
- markSchedulerSuspended,
33
- markSchedulerUnsuspended,
34
- markTaskStart,
35
- stopLoggingProfilingEvents,
36
- startLoggingProfilingEvents,
37
-} from '../SchedulerProfiling';
38
-
39
-import {enableIsInputPending} from '../SchedulerFeatureFlags';
40
-
41
-const perf = window.performance;
42
-
43
-function getCurrentTime() {
44
- return perf.now();
45
-}
46
-
47
-// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
48
-// Math.pow(2, 30) - 1
49
-// 0b111111111111111111111111111111
50
-var maxSigned31BitInt = 1073741823;
51
-
52
-// Times out immediately
53
-var IMMEDIATE_PRIORITY_TIMEOUT = -1;
54
-// Eventually times out
55
-var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
56
-var NORMAL_PRIORITY_TIMEOUT = 5000;
57
-var LOW_PRIORITY_TIMEOUT = 10000;
58
-// Never times out
59
-var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
60
-
61
-// Tasks are stored on a min heap
62
-var taskQueue = [];
63
-var timerQueue = [];
64
-
65
-// Incrementing id counter. Used to maintain insertion order.
66
-var taskIdCounter = 1;
67
-
68
-// Pausing the scheduler is useful for debugging.
69
-var isSchedulerPaused = false;
70
-
71
-var currentTask = null;
72
-var currentPriorityLevel = NormalPriority;
73
-
74
-// This is set while performing work, to prevent re-entrancy.
75
-var isPerformingWork = false;
76
-
77
-var isHostCallbackScheduled = false;
78
-var isHostTimeoutScheduled = false;
79
-
80
-// Capture local references to native APIs, in case a polyfill overrides them.
81
-const setTimeout = window.setTimeout;
82
-const clearTimeout = window.clearTimeout;
83
-
84
-if (typeof console !== 'undefined') {
85
- // TODO: Scheduler no longer requires these methods to be polyfilled. But
86
- // maybe we want to continue warning if they don't exist, to preserve the
87
- // option to rely on it in the future?
88
- const requestAnimationFrame = window.requestAnimationFrame;
89
- const cancelAnimationFrame = window.cancelAnimationFrame;
90
-
91
- if (typeof requestAnimationFrame !== 'function') {
92
- // Using console['error'] to evade Babel and ESLint
93
- console['error'](
94
- "This browser doesn't support requestAnimationFrame. " +
95
- 'Make sure that you load a ' +
96
- 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
97
- );
98
- }
99
- if (typeof cancelAnimationFrame !== 'function') {
100
- // Using console['error'] to evade Babel and ESLint
101
- console['error'](
102
- "This browser doesn't support cancelAnimationFrame. " +
103
- 'Make sure that you load a ' +
104
- 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
105
- );
106
- }
107
-}
108
-
109
-function advanceTimers(currentTime) {
110
- // Check for tasks that are no longer delayed and add them to the queue.
111
- let timer = peek(timerQueue);
112
- while (timer !== null) {
113
- if (timer.callback === null) {
114
- // Timer was cancelled.
115
- pop(timerQueue);
116
- } else if (timer.startTime <= currentTime) {
117
- // Timer fired. Transfer to the task queue.
118
- pop(timerQueue);
119
- timer.sortIndex = timer.expirationTime;
120
- push(taskQueue, timer);
121
- if (enableProfiling) {
122
- markTaskStart(timer, currentTime);
123
- timer.isQueued = true;
124
- }
125
- } else {
126
- // Remaining timers are pending.
127
- return;
128
- }
129
- timer = peek(timerQueue);
130
- }
131
-}
132
-
133
-function handleTimeout(currentTime) {
134
- isHostTimeoutScheduled = false;
135
- advanceTimers(currentTime);
136
-
137
- if (!isHostCallbackScheduled) {
138
- if (peek(taskQueue) !== null) {
139
- isHostCallbackScheduled = true;
140
- requestHostCallback(flushWork);
141
- } else {
142
- const firstTimer = peek(timerQueue);
143
- if (firstTimer !== null) {
144
- requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
145
- }
146
- }
147
- }
148
-}
149
-
150
-function flushWork(hasTimeRemaining, initialTime) {
151
- if (enableProfiling) {
152
- markSchedulerUnsuspended(initialTime);
153
- }
154
-
155
- // We'll need a host callback the next time work is scheduled.
156
- isHostCallbackScheduled = false;
157
- if (isHostTimeoutScheduled) {
158
- // We scheduled a timeout but it's no longer needed. Cancel it.
159
- isHostTimeoutScheduled = false;
160
- cancelHostTimeout();
161
- }
162
-
163
- isPerformingWork = true;
164
- const previousPriorityLevel = currentPriorityLevel;
165
- try {
166
- if (enableProfiling) {
167
- try {
168
- return workLoop(hasTimeRemaining, initialTime);
169
- } catch (error) {
170
- if (currentTask !== null) {
171
- const currentTime = getCurrentTime();
172
- markTaskErrored(currentTask, currentTime);
173
- currentTask.isQueued = false;
174
- }
175
- throw error;
176
- }
177
- } else {
178
- // No catch in prod code path.
179
- return workLoop(hasTimeRemaining, initialTime);
180
- }
181
- } finally {
182
- currentTask = null;
183
- currentPriorityLevel = previousPriorityLevel;
184
- isPerformingWork = false;
185
- if (enableProfiling) {
186
- const currentTime = getCurrentTime();
187
- markSchedulerSuspended(currentTime);
188
- }
189
- }
190
-}
191
-
192
-function workLoop(hasTimeRemaining, initialTime) {
193
- let currentTime = initialTime;
194
- advanceTimers(currentTime);
195
- currentTask = peek(taskQueue);
196
- while (
197
- currentTask !== null &&
198
- !(enableSchedulerDebugging && isSchedulerPaused)
199
- ) {
200
- if (
201
- currentTask.expirationTime > currentTime &&
202
- (!hasTimeRemaining || shouldYieldToHost())
203
- ) {
204
- // This currentTask hasn't expired, and we've reached the deadline.
205
- break;
206
- }
207
- const callback = currentTask.callback;
208
- if (typeof callback === 'function') {
209
- currentTask.callback = null;
210
- currentPriorityLevel = currentTask.priorityLevel;
211
- const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
212
- if (enableProfiling) {
213
- markTaskRun(currentTask, currentTime);
214
- }
215
- const continuationCallback = callback(didUserCallbackTimeout);
216
- currentTime = getCurrentTime();
217
- if (typeof continuationCallback === 'function') {
218
- currentTask.callback = continuationCallback;
219
- if (enableProfiling) {
220
- markTaskYield(currentTask, currentTime);
221
- }
222
- } else {
223
- if (enableProfiling) {
224
- markTaskCompleted(currentTask, currentTime);
225
- currentTask.isQueued = false;
226
- }
227
- if (currentTask === peek(taskQueue)) {
228
- pop(taskQueue);
229
- }
230
- }
231
- advanceTimers(currentTime);
232
- } else {
233
- pop(taskQueue);
234
- }
235
- currentTask = peek(taskQueue);
236
- }
237
- // Return whether there's additional work
238
- if (currentTask !== null) {
239
- return true;
240
- } else {
241
- const firstTimer = peek(timerQueue);
242
- if (firstTimer !== null) {
243
- requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
244
- }
245
- return false;
246
- }
247
-}
248
-
249
-function unstable_runWithPriority(priorityLevel, eventHandler) {
250
- switch (priorityLevel) {
251
- case ImmediatePriority:
252
- case UserBlockingPriority:
253
- case NormalPriority:
254
- case LowPriority:
255
- case IdlePriority:
256
- break;
257
- default:
258
- priorityLevel = NormalPriority;
259
- }
260
-
261
- var previousPriorityLevel = currentPriorityLevel;
262
- currentPriorityLevel = priorityLevel;
263
-
264
- try {
265
- return eventHandler();
266
- } finally {
267
- currentPriorityLevel = previousPriorityLevel;
268
- }
269
-}
270
-
271
-function unstable_next(eventHandler) {
272
- var priorityLevel;
273
- switch (currentPriorityLevel) {
274
- case ImmediatePriority:
275
- case UserBlockingPriority:
276
- case NormalPriority:
277
- // Shift down to normal priority
278
- priorityLevel = NormalPriority;
279
- break;
280
- default:
281
- // Anything lower than normal priority should remain at the current level.
282
- priorityLevel = currentPriorityLevel;
283
- break;
284
- }
285
-
286
- var previousPriorityLevel = currentPriorityLevel;
287
- currentPriorityLevel = priorityLevel;
288
-
289
- try {
290
- return eventHandler();
291
- } finally {
292
- currentPriorityLevel = previousPriorityLevel;
293
- }
294
-}
295
-
296
-function unstable_wrapCallback(callback) {
297
- var parentPriorityLevel = currentPriorityLevel;
298
- return function() {
299
- // This is a fork of runWithPriority, inlined for performance.
300
- var previousPriorityLevel = currentPriorityLevel;
301
- currentPriorityLevel = parentPriorityLevel;
302
-
303
- try {
304
- return callback.apply(this, arguments);
305
- } finally {
306
- currentPriorityLevel = previousPriorityLevel;
307
- }
308
- };
309
-}
310
-
311
-function unstable_scheduleCallback(priorityLevel, callback, options) {
312
- var currentTime = getCurrentTime();
313
-
314
- var startTime;
315
- if (typeof options === 'object' && options !== null) {
316
- var delay = options.delay;
317
- if (typeof delay === 'number' && delay > 0) {
318
- startTime = currentTime + delay;
319
- } else {
320
- startTime = currentTime;
321
- }
322
- } else {
323
- startTime = currentTime;
324
- }
325
-
326
- var timeout;
327
- switch (priorityLevel) {
328
- case ImmediatePriority:
329
- timeout = IMMEDIATE_PRIORITY_TIMEOUT;
330
- break;
331
- case UserBlockingPriority:
332
- timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
333
- break;
334
- case IdlePriority:
335
- timeout = IDLE_PRIORITY_TIMEOUT;
336
- break;
337
- case LowPriority:
338
- timeout = LOW_PRIORITY_TIMEOUT;
339
- break;
340
- case NormalPriority:
341
- default:
342
- timeout = NORMAL_PRIORITY_TIMEOUT;
343
- break;
344
- }
345
-
346
- var expirationTime = startTime + timeout;
347
-
348
- var newTask = {
349
- id: taskIdCounter++,
350
- callback,
351
- priorityLevel,
352
- startTime,
353
- expirationTime,
354
- sortIndex: -1,
355
- };
356
- if (enableProfiling) {
357
- newTask.isQueued = false;
358
- }
359
-
360
- if (startTime > currentTime) {
361
- // This is a delayed task.
362
- newTask.sortIndex = startTime;
363
- push(timerQueue, newTask);
364
- if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
365
- // All tasks are delayed, and this is the task with the earliest delay.
366
- if (isHostTimeoutScheduled) {
367
- // Cancel an existing timeout.
368
- cancelHostTimeout();
369
- } else {
370
- isHostTimeoutScheduled = true;
371
- }
372
- // Schedule a timeout.
373
- requestHostTimeout(handleTimeout, startTime - currentTime);
374
- }
375
- } else {
376
- newTask.sortIndex = expirationTime;
377
- push(taskQueue, newTask);
378
- if (enableProfiling) {
379
- markTaskStart(newTask, currentTime);
380
- newTask.isQueued = true;
381
- }
382
- // Schedule a host callback, if needed. If we're already performing work,
383
- // wait until the next time we yield.
384
- if (!isHostCallbackScheduled && !isPerformingWork) {
385
- isHostCallbackScheduled = true;
386
- requestHostCallback(flushWork);
387
- }
388
- }
389
-
390
- return newTask;
391
-}
392
-
393
-function unstable_pauseExecution() {
394
- isSchedulerPaused = true;
395
-}
396
-
397
-function unstable_continueExecution() {
398
- isSchedulerPaused = false;
399
- if (!isHostCallbackScheduled && !isPerformingWork) {
400
- isHostCallbackScheduled = true;
401
- requestHostCallback(flushWork);
402
- }
403
-}
404
-
405
-function unstable_getFirstCallbackNode() {
406
- return peek(taskQueue);
407
-}
408
-
409
-function unstable_cancelCallback(task) {
410
- if (enableProfiling) {
411
- if (task.isQueued) {
412
- const currentTime = getCurrentTime();
413
- markTaskCanceled(task, currentTime);
414
- task.isQueued = false;
415
- }
416
- }
417
-
418
- // Null out the callback to indicate the task has been canceled. (Can't
419
- // remove from the queue because you can't remove arbitrary nodes from an
420
- // array based heap, only the first one.)
421
- task.callback = null;
422
-}
423
-
424
-function unstable_getCurrentPriorityLevel() {
425
- return currentPriorityLevel;
426
-}
427
-
428
-let isTaskLoopRunning = false;
429
-let scheduledHostCallback = null;
430
-let taskTimeoutID = -1;
431
-
432
-// Scheduler periodically yields in case there is other work on the main
433
-// thread, like user events. By default, it yields multiple times per frame.
434
-// It does not attempt to align with frame boundaries, since most tasks don't
435
-// need to be frame aligned; for those that do, use requestAnimationFrame.
436
-let yieldInterval = 5;
437
-let deadline = 0;
438
-
439
-// TODO: Make this configurable
440
-// TODO: Adjust this based on priority?
441
-const maxYieldInterval = 300;
442
-let needsPaint = false;
443
-
444
-function shouldYieldToHost() {
445
- if (
446
- enableIsInputPending &&
447
- navigator !== undefined &&
448
- navigator.scheduling !== undefined &&
449
- navigator.scheduling.isInputPending !== undefined
450
- ) {
451
- const scheduling = navigator.scheduling;
452
- const currentTime = getCurrentTime();
453
- if (currentTime >= deadline) {
454
- // There's no time left. We may want to yield control of the main
455
- // thread, so the browser can perform high priority tasks. The main ones
456
- // are painting and user input. If there's a pending paint or a pending
457
- // input, then we should yield. But if there's neither, then we can
458
- // yield less often while remaining responsive. We'll eventually yield
459
- // regardless, since there could be a pending paint that wasn't
460
- // accompanied by a call to `requestPaint`, or other main thread tasks
461
- // like network events.
462
- if (needsPaint || scheduling.isInputPending()) {
463
- // There is either a pending paint or a pending input.
464
- return true;
465
- }
466
- // There's no pending input. Only yield if we've reached the max
467
- // yield interval.
468
- return currentTime >= maxYieldInterval;
469
- } else {
470
- // There's still time left in the frame.
471
- return false;
472
- }
473
- } else {
474
- // `isInputPending` is not available. Since we have no way of knowing if
475
- // there's pending input, always yield at the end of the frame.
476
- return getCurrentTime() >= deadline;
477
- }
478
-}
479
-
480
-function requestPaint() {
481
- if (
482
- enableIsInputPending &&
483
- navigator !== undefined &&
484
- navigator.scheduling !== undefined &&
485
- navigator.scheduling.isInputPending !== undefined
486
- ) {
487
- needsPaint = true;
488
- }
489
-
490
- // Since we yield every frame regardless, `requestPaint` has no effect.
491
-}
492
-
493
-function forceFrameRate(fps) {
494
- if (fps < 0 || fps > 125) {
495
- // Using console['error'] to evade Babel and ESLint
496
- console['error'](
497
- 'forceFrameRate takes a positive int between 0 and 125, ' +
498
- 'forcing frame rates higher than 125 fps is not supported',
499
- );
500
- return;
501
- }
502
- if (fps > 0) {
503
- yieldInterval = Math.floor(1000 / fps);
504
- } else {
505
- // reset the framerate
506
- yieldInterval = 5;
507
- }
508
-}
509
-
510
-const performWorkUntilDeadline = () => {
511
- if (scheduledHostCallback !== null) {
512
- const currentTime = getCurrentTime();
513
- // Yield after `yieldInterval` ms, regardless of where we are in the vsync
514
- // cycle. This means there's always time remaining at the beginning of
515
- // the message event.
516
- deadline = currentTime + yieldInterval;
517
- const hasTimeRemaining = true;
518
-
519
- // If a scheduler task throws, exit the current browser task so the
520
- // error can be observed.
521
- //
522
- // Intentionally not using a try-catch, since that makes some debugging
523
- // techniques harder. Instead, if `scheduledHostCallback` errors, then
524
- // `hasMoreWork` will remain true, and we'll continue the work loop.
525
- let hasMoreWork = true;
526
- try {
527
- hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
528
- } finally {
529
- if (hasMoreWork) {
530
- // If there's more work, schedule the next browser task at the end of
531
- // the preceding one.
532
- postTask(performWorkUntilDeadline);
533
- } else {
534
- isTaskLoopRunning = false;
535
- scheduledHostCallback = null;
536
- }
537
- }
538
- } else {
539
- isTaskLoopRunning = false;
540
- }
541
- // Yielding to the browser will give it a chance to paint, so we can
542
- // reset this.
543
- needsPaint = false;
544
-};
545
-
546
-function postTask(callback) {
547
- // Use experimental Chrome Scheduler postTask API.
548
- global.scheduler.postTask(callback);
549
-}
550
-
551
-function requestHostCallback(callback) {
552
- scheduledHostCallback = callback;
553
- if (!isTaskLoopRunning) {
554
- isTaskLoopRunning = true;
555
- postTask(performWorkUntilDeadline);
556
- }
557
-}
558
-
559
-function requestHostTimeout(callback, ms) {
560
- taskTimeoutID = setTimeout(() => {
561
- callback(getCurrentTime());
562
- }, ms);
563
-}
564
-
565
-function cancelHostTimeout() {
566
- clearTimeout(taskTimeoutID);
567
- taskTimeoutID = -1;
568
-}
569
-
570
-const unstable_requestPaint = requestPaint;
571
-
572
-export {
573
- ImmediatePriority as unstable_ImmediatePriority,
574
- UserBlockingPriority as unstable_UserBlockingPriority,
575
- NormalPriority as unstable_NormalPriority,
576
- IdlePriority as unstable_IdlePriority,
577
- LowPriority as unstable_LowPriority,
578
- unstable_runWithPriority,
579
- unstable_next,
580
- unstable_scheduleCallback,
581
- unstable_cancelCallback,
582
- unstable_wrapCallback,
583
- unstable_getCurrentPriorityLevel,
584
- shouldYieldToHost as unstable_shouldYield,
585
- unstable_requestPaint,
586
- unstable_continueExecution,
587
- unstable_pauseExecution,
588
- unstable_getFirstCallbackNode,
589
- getCurrentTime as unstable_now,
590
- forceFrameRate as unstable_forceFrameRate,
591
-};
592
-
593
-export const unstable_Profiling = enableProfiling
594
- ? {
595
- startLoggingProfilingEvents,
596
- stopLoggingProfilingEvents,
597
- }
598
- : null;
packages/scheduler/unstable_no_dom.js
deleted
-10
@@ -1,10 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
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
-
8
-'use strict';
9
-
10
-export * from './src/forks/SchedulerNoDOM';
packages/scheduler/unstable_post_task_only.js
deleted
-10
@@ -1,10 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
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
-
8
-'use strict';
9
-
10
-export * from './src/forks/SchedulerPostTaskOnly';
packages/shared/__tests__/ReactDOMFrameScheduling-test.js
+1
-32
@@ -13,43 +13,14 @@ describe('ReactDOMFrameScheduling', () => {
13
beforeEach(() => {
14
jest.resetModules();
15
16
- // Un-mock scheduler
17
- jest.mock('scheduler', () => require.requireActual('scheduler'));
18
- });
19
-
20
- it('warns when requestAnimationFrame is not polyfilled in the browser', () => {
21
- const previousRAF = global.requestAnimationFrame;
22
- const previousMessageChannel = global.MessageChannel;
23
- try {
24
- delete global.requestAnimationFrame;
25
- global.MessageChannel = function MessageChannel() {
26
- return {
27
- port1: {},
28
- port2: {},
29
- };
30
- };
31
- spyOnDevAndProd(console, 'error');
32
- require('react-dom');
33
- expect(console.error.calls.count()).toEqual(1);
34
- expect(console.error.calls.argsFor(0)[0]).toMatch(
35
- "This browser doesn't support requestAnimationFrame.",
36
- );
37
- } finally {
38
- global.MessageChannel = previousMessageChannel;
39
- global.requestAnimationFrame = previousRAF;
40
- }
16
+ jest.unmock('scheduler');
17
});
18
19
// We're just testing importing, not using it.
20
// It is important because even isomorphic components may import it.
45
- // @gate !source
21
it('can import findDOMNode in Node environment', () => {
47
- const previousRAF = global.requestAnimationFrame;
48
- const previousRIC = global.requestIdleCallback;
22
const prevWindow = global.window;
23
try {
51
- global.requestAnimationFrame = undefined;
52
- global.requestIdleCallback = undefined;
24
// Simulate the Node environment:
25
delete global.window;
26
jest.resetModules();
@@ -57,8 +28,6 @@ describe('ReactDOMFrameScheduling', () => {
28
require('react-dom');
29
}).not.toThrow();
30
} finally {
60
- global.requestAnimationFrame = previousRAF;
61
- global.requestIdleCallback = previousRIC;
31
global.window = prevWindow;
32
}
33
});
scripts/rollup/bundles.js
-33
@@ -735,39 +735,6 @@ const bundles = [
735
externals: [],
736
},
737
738
- /******* React Scheduler Post Task Only (experimental) *******/
739
- {
740
- bundleTypes: [
741
- NODE_DEV,
742
- NODE_PROD,
743
- FB_WWW_DEV,
744
- FB_WWW_PROD,
745
- FB_WWW_PROFILING,
746
- ],
747
- moduleType: ISOMORPHIC,
748
- entry: 'scheduler/unstable_post_task_only',
749
- global: 'SchedulerPostTaskOnly',
750
- externals: [],
751
- },
752
-
753
- /******* React Scheduler No DOM (experimental) *******/
754
- {
755
- bundleTypes: [
756
- NODE_DEV,
757
- NODE_PROD,
758
- FB_WWW_DEV,
759
- FB_WWW_PROD,
760
- FB_WWW_PROFILING,
761
- RN_FB_DEV,
762
- RN_FB_PROD,
763
- RN_FB_PROFILING,
764
- ],
765
- moduleType: ISOMORPHIC,
766
- entry: 'scheduler/unstable_no_dom',
767
- global: 'SchedulerNoDOM',
768
- externals: [],
769
- },
770
-
738
/******* Jest React (experimental) *******/
739
{
740
bundleTypes: [NODE_DEV, NODE_PROD],
scripts/rollup/validate/eslintrc.rn.js
+2
-1
@@ -24,7 +24,8 @@ module.exports = {
24
nativeFabricUIManager: true,
25
// Trusted Types
26
trustedTypes: true,
27
-
27
+ // RN supports this
28
+ setImmediate: true,
29
// Scheduler profiling
30
Int32Array: true,
31
ArrayBuffer: true,
scripts/rollup/validate/eslintrc.umd.js
+2
@@ -26,6 +26,8 @@ module.exports = {
26
require: true,
27
globalThis: true,
28
global: true,
29
+ // Internet Explorer
30
+ setImmediate: true,
31
// Trusted Types
32
trustedTypes: true,
33