Skip to content

Commit 65c4e55

Browse files
committed
[scheduler] Priority levels, continuations, and wrapped callbacks
All of these features are based on features of React's internal scheduler. The eventual goal is to lift as much as possible out of the React internals into the Scheduler package. Includes some renaming of existing methods. - `scheduleWork` is now `scheduleCallback` - `cancelScheduledWork` is now `cancelCallback` Priority levels --------------- Adds the ability to schedule callbacks at different priority levels. The current levels are (final names TBD): - Immediate priority. Fires at the end of the outermost currently executing (similar to a microtask). - Interactive priority. Fires within a few hundred milliseconds. This should only be used to provide quick feedback to the user as a result of an interaction. - Normal priority. This is the default. Fires within several seconds. - "Maybe" priority. Only fires if there's nothing else to do. Used for prerendering or warming a cache. The priority is changed using `runWithPriority`: ```js runWithPriority(InteractivePriority, () => { scheduleCallback(callback); }); ``` Continuations ------------- Adds the ability for a callback to yield without losing its place in the queue, by returning a continuation. The continuation will have the same expiration as the callback that yielded. Wrapped callbacks ----------------- Adds the ability to wrap a callback so that, when it is called, it receives the priority of the current execution context.
1 parent 970a34b commit 65c4e55

File tree

15 files changed

+913
-301
lines changed

15 files changed

+913
-301
lines changed

fixtures/scheduler/index.html

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<!DOCTYPE html>
22
<html style="width: 100%; height: 100%;">
3-
<head>
4-
<meta charset="utf-8">
5-
<title>Scheduler Test Page</title>
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Scheduler Test Page</title>
67
<style>
78
.correct {
89
border: solid green 2px;
@@ -11,91 +12,92 @@
1112
border: dashed red 2px;
1213
}
1314
</style>
14-
</head>
15-
<body>
16-
<h1>Scheduler Fixture</h1>
17-
<p>
18-
This fixture is for manual testing purposes, and the patterns used in
19-
implementing it should not be used as a model. This is mainly for anyone
20-
working on making changes to the `schedule` module.
21-
</p>
22-
<h2>Tests:</h2>
23-
<ol>
24-
<li>
25-
<button onClick="runTestOne()">Run Test 1</button>
26-
<p>Calls the callback within the frame when not blocked:</p>
27-
<div><b>Expected:</b></div>
28-
<div id="test-1-expected">
29-
</div>
30-
<div> -------------------------------------------------</div>
31-
<div> If you see the same above and below it's correct.
15+
</head>
16+
17+
<body>
18+
<h1>Scheduler Fixture</h1>
19+
<p>
20+
This fixture is for manual testing purposes, and the patterns used in
21+
implementing it should not be used as a model. This is mainly for anyone
22+
working on making changes to the `schedule` module.
23+
</p>
24+
<h2>Tests:</h2>
25+
<ol>
26+
<li>
27+
<button onClick="runTestOne()">Run Test 1</button>
28+
<p>Calls the callback within the frame when not blocked:</p>
29+
<div><b>Expected:</b></div>
30+
<div id="test-1-expected">
31+
</div>
32+
<div> -------------------------------------------------</div>
33+
<div> If you see the same above and below it's correct.
3234
<div> -------------------------------------------------</div>
3335
<div><b>Actual:</b></div>
3436
<div id="test-1"></div>
35-
</li>
36-
<li>
37-
<p>Accepts multiple callbacks and calls within frame when not blocked</p>
38-
<button onClick="runTestTwo()">Run Test 2</button>
39-
<div><b>Expected:</b></div>
40-
<div id="test-2-expected">
41-
</div>
42-
<div> -------------------------------------------------</div>
43-
<div> If you see the same above and below it's correct.
37+
</li>
38+
<li>
39+
<p>Accepts multiple callbacks and calls within frame when not blocked</p>
40+
<button onClick="runTestTwo()">Run Test 2</button>
41+
<div><b>Expected:</b></div>
42+
<div id="test-2-expected">
43+
</div>
44+
<div> -------------------------------------------------</div>
45+
<div> If you see the same above and below it's correct.
4446
<div> -------------------------------------------------</div>
4547
<div><b>Actual:</b></div>
4648
<div id="test-2"></div>
47-
</li>
48-
<li>
49-
<p>Schedules callbacks in correct order when they use scheduleWork to schedule themselves</p>
50-
<button onClick="runTestThree()">Run Test 3</button>
51-
<div><b>Expected:</b></div>
52-
<div id="test-3-expected">
53-
</div>
54-
<div> -------------------------------------------------</div>
55-
<div> If you see the same above and below it's correct.
49+
</li>
50+
<li>
51+
<p>Schedules callbacks in correct order when they use scheduleWork to schedule themselves</p>
52+
<button onClick="runTestThree()">Run Test 3</button>
53+
<div><b>Expected:</b></div>
54+
<div id="test-3-expected">
55+
</div>
56+
<div> -------------------------------------------------</div>
57+
<div> If you see the same above and below it's correct.
5658
<div> -------------------------------------------------</div>
5759
<div><b>Actual:</b></div>
5860
<div id="test-3"></div>
59-
</li>
60-
<li>
61-
<p>Calls timed out callbacks and then any more pending callbacks, defers others if time runs out</p>
62-
<button onClick="runTestFour()">Run Test 4</button>
63-
<div><b>Expected:</b></div>
64-
<div id="test-4-expected">
65-
</div>
66-
<div> -------------------------------------------------</div>
67-
<div> If you see the same above and below it's correct.
61+
</li>
62+
<li>
63+
<p>Calls timed out callbacks and then any more pending callbacks, defers others if time runs out</p>
64+
<button onClick="runTestFour()">Run Test 4</button>
65+
<div><b>Expected:</b></div>
66+
<div id="test-4-expected">
67+
</div>
68+
<div> -------------------------------------------------</div>
69+
<div> If you see the same above and below it's correct.
6870
<div> -------------------------------------------------</div>
6971
<div><b>Actual:</b></div>
7072
<div id="test-4"></div>
71-
</li>
72-
<li>
73-
<p>When some callbacks throw errors, still calls them all within the same frame</p>
74-
<p><b>IMPORTANT:</b> Open the console when you run this! Inspect the logs there!</p>
75-
<button onClick="runTestFive()">Run Test 5</button>
76-
</li>
77-
<li>
78-
<p>When some callbacks throw errors <b> and some also time out</b>, still calls them all within the same frame</p>
79-
<p><b>IMPORTANT:</b> Open the console when you run this! Inspect the logs there!</p>
80-
<button onClick="runTestSix()">Run Test 6</button>
81-
</li>
82-
<li>
83-
<p>Continues calling callbacks even when user switches away from this tab</p>
84-
<button onClick="runTestSeven()">Run Test 7</button>
85-
<div><b>Click the button above, observe the counter, then switch to
86-
another tab and switch back:</b></div>
87-
<div id="test-7">
88-
</div>
89-
<div> If the counter advanced while you were away from this tab, it's correct.</div>
90-
</li>
91-
</ol>
92-
<script src="../../build/dist/react.development.js"></script>
93-
<script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
94-
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
95-
<script type="text/babel">
73+
</li>
74+
<li>
75+
<p>When some callbacks throw errors, still calls them all within the same frame</p>
76+
<p><b>IMPORTANT:</b> Open the console when you run this! Inspect the logs there!</p>
77+
<button onClick="runTestFive()">Run Test 5</button>
78+
</li>
79+
<li>
80+
<p>When some callbacks throw errors <b> and some also time out</b>, still calls them all within the same frame</p>
81+
<p><b>IMPORTANT:</b> Open the console when you run this! Inspect the logs there!</p>
82+
<button onClick="runTestSix()">Run Test 6</button>
83+
</li>
84+
<li>
85+
<p>Continues calling callbacks even when user switches away from this tab</p>
86+
<button onClick="runTestSeven()">Run Test 7</button>
87+
<div><b>Click the button above, observe the counter, then switch to
88+
another tab and switch back:</b></div>
89+
<div id="test-7">
90+
</div>
91+
<div> If the counter advanced while you were away from this tab, it's correct.</div>
92+
</li>
93+
</ol>
94+
<script src="../../build/dist/react.development.js"></script>
95+
<script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
96+
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
97+
<script type="text/babel">
9698
const {
97-
unstable_scheduleWork: scheduleWork,
98-
unstable_cancelWork: cancelWork,
99+
unstable_scheduleCallback: scheduleCallback,
100+
unstable_cancelCallback: cancelCallback,
99101
unstable_now: now
100102
} = Scheduler;
101103
function displayTestResult(testNumber) {
@@ -496,4 +498,4 @@ <h2>Tests:</h2>
496498
}
497499
</script type="text/babel">
498500
</body>
499-
</html>
501+
</html>

fixtures/tracing/script.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ function checkSchedulerAPI() {
3030
if (
3131
typeof Scheduler === 'undefined' ||
3232
typeof Scheduler.unstable_now !== 'function' ||
33-
typeof Scheduler.unstable_scheduleWork !== 'function' ||
34-
typeof Scheduler.unstable_cancelScheduledWork !== 'function'
33+
typeof Scheduler.unstable_scheduleCallback !== 'function' ||
34+
typeof Scheduler.unstable_cancelCallback !== 'function'
3535
) {
3636
throw 'API is not defined';
3737
}

fixtures/unstable-async/suspense/src/components/App.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, {Placeholder, PureComponent} from 'react';
2-
import {unstable_scheduleWork} from 'scheduler';
2+
import {unstable_scheduleCallback} from 'scheduler';
33
import {
44
unstable_trace as trace,
55
unstable_wrap as wrap,
@@ -38,7 +38,7 @@ export default class App extends PureComponent {
3838
currentId: id,
3939
})
4040
);
41-
unstable_scheduleWork(
41+
unstable_scheduleCallback(
4242
wrap(() =>
4343
trace(`View ${id} (low-pri)`, performance.now(), () =>
4444
this.setState({

fixtures/unstable-async/time-slicing/src/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {PureComponent} from 'react';
22
import {flushSync, render} from 'react-dom';
3-
import {unstable_scheduleWork} from 'scheduler';
3+
import {unstable_scheduleCallback} from 'scheduler';
44
import _ from 'lodash';
55
import Charts from './Charts';
66
import Clock from './Clock';
@@ -67,7 +67,7 @@ class App extends PureComponent {
6767
}
6868
this._ignoreClick = true;
6969

70-
unstable_scheduleWork(() => {
70+
unstable_scheduleCallback(() => {
7171
this.setState({showDemo: true}, () => {
7272
this._ignoreClick = false;
7373
});
@@ -107,7 +107,7 @@ class App extends PureComponent {
107107
this.debouncedHandleChange(value);
108108
break;
109109
case 'async':
110-
unstable_scheduleWork(() => {
110+
unstable_scheduleCallback(() => {
111111
this.setState({value});
112112
});
113113
break;

packages/react-art/src/ReactARTHostConfig.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
export {
99
unstable_now as now,
10-
unstable_scheduleWork as scheduleDeferredCallback,
11-
unstable_cancelScheduledWork as cancelDeferredCallback,
10+
unstable_scheduleCallback as scheduleDeferredCallback,
11+
unstable_cancelCallback as cancelDeferredCallback,
1212
} from 'scheduler';
1313
import Transform from 'art/core/transform';
1414
import Mode from 'art/modes/current';

packages/react-dom/src/client/ReactDOMHostConfig.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ export type NoTimeout = -1;
6262

6363
export {
6464
unstable_now as now,
65-
unstable_scheduleWork as scheduleDeferredCallback,
66-
unstable_cancelScheduledWork as cancelDeferredCallback,
65+
unstable_scheduleCallback as scheduleDeferredCallback,
66+
unstable_cancelCallback as cancelDeferredCallback,
6767
} from 'scheduler';
6868

6969
let SUPPRESS_HYDRATION_WARNING;

packages/react/src/ReactSharedInternals.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77

88
import assign from 'object-assign';
99
import {
10-
unstable_cancelScheduledWork,
10+
unstable_cancelCallback,
1111
unstable_now,
12-
unstable_scheduleWork,
12+
unstable_scheduleCallback,
13+
unstable_runWithPriority,
14+
unstable_wrapCallback,
15+
unstable_getCurrentPriorityLevel,
1316
} from 'scheduler';
1417
import {
1518
__interactionsRef,
@@ -39,9 +42,12 @@ if (__UMD__) {
3942
// CJS bundles use the shared NPM package.
4043
Object.assign(ReactSharedInternals, {
4144
Scheduler: {
42-
unstable_cancelScheduledWork,
45+
unstable_cancelCallback,
4346
unstable_now,
44-
unstable_scheduleWork,
47+
unstable_scheduleCallback,
48+
unstable_runWithPriority,
49+
unstable_wrapCallback,
50+
unstable_getCurrentPriorityLevel,
4551
},
4652
SchedulerTracing: {
4753
__interactionsRef,

packages/scheduler/npm/umd/scheduler.development.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* LICENSE file in the root directory of this source tree.
88
*/
99

10+
/* eslint-disable max-len */
11+
1012
'use strict';
1113

1214
(function(global, factory) {
@@ -23,23 +25,47 @@
2325
);
2426
}
2527

26-
function unstable_scheduleWork() {
27-
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_scheduleWork.apply(
28+
function unstable_scheduleCallback() {
29+
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_scheduleCallback.apply(
30+
this,
31+
arguments
32+
);
33+
}
34+
35+
function unstable_cancelCallback() {
36+
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_cancelCallback.apply(
37+
this,
38+
arguments
39+
);
40+
}
41+
42+
function unstable_runWithPriority() {
43+
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_runWithPriority.apply(
44+
this,
45+
arguments
46+
);
47+
}
48+
49+
function unstable_wrapCallback() {
50+
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_wrapCallback.apply(
2851
this,
2952
arguments
3053
);
3154
}
3255

33-
function unstable_cancelScheduledWork() {
34-
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_cancelScheduledWork.apply(
56+
function unstable_getCurrentPriorityLevel() {
57+
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_getCurrentPriorityLevel.apply(
3558
this,
3659
arguments
3760
);
3861
}
3962

4063
return Object.freeze({
4164
unstable_now: unstable_now,
42-
unstable_scheduleWork: unstable_scheduleWork,
43-
unstable_cancelScheduledWork: unstable_cancelScheduledWork,
65+
unstable_scheduleCallback: unstable_scheduleCallback,
66+
unstable_cancelCallback: unstable_cancelCallback,
67+
unstable_runWithPriority: unstable_runWithPriority,
68+
unstable_wrapCallback: unstable_wrapCallback,
69+
unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
4470
});
4571
});

0 commit comments

Comments
 (0)