@samitouri / QOS-React / commits / b8ed6a1aa5

[Scheduler] Call postTask directly (#19551)

This updates the experimental Scheduler postTask build to call postTask directly, instead of managing our own custom queue and work loop. We still use a deadline 5ms mechanism to implement `shouldYield`. The main thing that postTask is currently missing is the continuation feature — when yielding to the main thread, the yielding task is sent to the back of the queue, instead of maintaining its position. While this would be nice to have, even without it, postTask may be good enough to replace our userspace implementation. We'll run some tests to see.

Andrew Clark committed Aug 12, 2020 at 08:39 UTC b8ed6a1aa580e4de80f707293015d638d3252d63
11 files changed +400 -200
.eslintrc.js
+6
@@ -166,6 +166,12 @@ module.exports = {
166 __webpack_require__: true,
167 },
168 },
169 + {
170 + files: ['packages/scheduler/**/*.js'],
171 + globals: {
172 + TaskController: true,
173 + },
174 + },
175 ],
176
177 globals: {
packages/scheduler/src/SchedulerPostTask.js new
+249
@@ -0,0 +1,249 @@
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 + * @flow
8 + */
9 +
10 +import type {PriorityLevel} from './SchedulerPriorities';
11 +
12 +declare class TaskController {
13 + constructor(priority?: string): TaskController;
14 + signal: mixed;
15 + abort(): void;
16 +}
17 +
18 +type PostTaskPriorityLevel = 'user-blocking' | 'user-visible' | 'background';
19 +
20 +type CallbackNode = {|
21 + _controller: TaskController,
22 +|};
23 +
24 +import {
25 + ImmediatePriority,
26 + UserBlockingPriority,
27 + NormalPriority,
28 + LowPriority,
29 + IdlePriority,
30 +} from './SchedulerPriorities';
31 +
32 +export {
33 + ImmediatePriority as unstable_ImmediatePriority,
34 + UserBlockingPriority as unstable_UserBlockingPriority,
35 + NormalPriority as unstable_NormalPriority,
36 + IdlePriority as unstable_IdlePriority,
37 + LowPriority as unstable_LowPriority,
38 +};
39 +
40 +// Capture local references to native APIs, in case a polyfill overrides them.
41 +const perf = window.performance;
42 +
43 +// Use experimental Chrome Scheduler postTask API.
44 +const scheduler = global.scheduler;
45 +
46 +const getCurrentTime = perf.now.bind(perf);
47 +
48 +export const unstable_now = getCurrentTime;
49 +
50 +// Scheduler periodically yields in case there is other work on the main
51 +// thread, like user events. By default, it yields multiple times per frame.
52 +// It does not attempt to align with frame boundaries, since most tasks don't
53 +// need to be frame aligned; for those that do, use requestAnimationFrame.
54 +const yieldInterval = 5;
55 +let deadline = 0;
56 +
57 +let currentPriorityLevel_DEPRECATED = NormalPriority;
58 +
59 +// `isInputPending` is not available. Since we have no way of knowing if
60 +// there's pending input, always yield at the end of the frame.
61 +export function unstable_shouldYield() {
62 + return getCurrentTime() >= deadline;
63 +}
64 +
65 +export function unstable_requestPaint() {
66 + // Since we yield every frame regardless, `requestPaint` has no effect.
67 +}
68 +
69 +type SchedulerCallback<T> = (
70 + didTimeout_DEPRECATED: boolean,
71 +) =>
72 + | T
73 + // May return a continuation
74 + | SchedulerCallback<T>;
75 +
76 +export function unstable_scheduleCallback<T>(
77 + priorityLevel: PriorityLevel,
78 + callback: SchedulerCallback<T>,
79 + options?: {delay?: number},
80 +): CallbackNode {
81 + let postTaskPriority;
82 + switch (priorityLevel) {
83 + case ImmediatePriority:
84 + case UserBlockingPriority:
85 + postTaskPriority = 'user-blocking';
86 + break;
87 + case LowPriority:
88 + case NormalPriority:
89 + postTaskPriority = 'user-visible';
90 + break;
91 + case IdlePriority:
92 + postTaskPriority = 'background';
93 + break;
94 + default:
95 + postTaskPriority = 'user-visible';
96 + break;
97 + }
98 +
99 + const controller = new TaskController();
100 + const postTaskOptions = {
101 + priority: postTaskPriority,
102 + delay: typeof options === 'object' && options !== null ? options.delay : 0,
103 + signal: controller.signal,
104 + };
105 +
106 + const node = {
107 + _controller: controller,
108 + };
109 +
110 + scheduler
111 + .postTask(
112 + runTask.bind(null, priorityLevel, postTaskPriority, node, callback),
113 + postTaskOptions,
114 + )
115 + .catch(handlePostTaskError);
116 +
117 + return node;
118 +}
119 +
120 +function runTask<T>(
121 + priorityLevel: PriorityLevel,
122 + postTaskPriority: PostTaskPriorityLevel,
123 + node: CallbackNode,
124 + callback: SchedulerCallback<T>,
125 +) {
126 + deadline = getCurrentTime() + yieldInterval;
127 + try {
128 + currentPriorityLevel_DEPRECATED = priorityLevel;
129 + const didTimeout_DEPRECATED = false;
130 + const result = callback(didTimeout_DEPRECATED);
131 + if (typeof result === 'function') {
132 + // Assume this is a continuation
133 + const continuation: SchedulerCallback<T> = (result: any);
134 + const continuationController = new TaskController();
135 + const continuationOptions = {
136 + priority: postTaskPriority,
137 + signal: continuationController.signal,
138 + };
139 + // Update the original callback node's controller, since even though we're
140 + // posting a new task, conceptually it's the same one.
141 + node._controller = continuationController;
142 + scheduler
143 + .postTask(
144 + runTask.bind(
145 + null,
146 + priorityLevel,
147 + postTaskPriority,
148 + node,
149 + continuation,
150 + ),
151 + continuationOptions,
152 + )
153 + .catch(handlePostTaskError);
154 + }
155 + } finally {
156 + currentPriorityLevel_DEPRECATED = NormalPriority;
157 + }
158 +}
159 +
160 +function handlePostTaskError(error) {
161 + // This error is either a user error thrown by a callback, or an AbortError
162 + // as a result of a cancellation.
163 + //
164 + // User errors trigger a global `error` event even if we don't rethrow them.
165 + // In fact, if we do rethrow them, they'll get reported to the console twice.
166 + // I'm not entirely sure the current `postTask` spec makes sense here. If I
167 + // catch a `postTask` call, it shouldn't trigger a global error.
168 + //
169 + // Abort errors are an implementation detail. We don't expose the
170 + // TaskController to the user, nor do we expose the promise that is returned
171 + // from `postTask`. So we shouldn't rethrow those, either, since there's no
172 + // way to handle them. (If we did return the promise to the user, then it
173 + // should be up to them to handle the AbortError.)
174 + //
175 + // In either case, we can suppress the error, barring changes to the spec
176 + // or the Scheduler API.
177 +}
178 +
179 +export function unstable_cancelCallback(node: CallbackNode) {
180 + const controller = node._controller;
181 + controller.abort();
182 +}
183 +
184 +export function unstable_runWithPriority<T>(
185 + priorityLevel: PriorityLevel,
186 + callback: () => T,
187 +): T {
188 + const previousPriorityLevel = currentPriorityLevel_DEPRECATED;
189 + currentPriorityLevel_DEPRECATED = priorityLevel;
190 + try {
191 + return callback();
192 + } finally {
193 + currentPriorityLevel_DEPRECATED = previousPriorityLevel;
194 + }
195 +}
196 +
197 +export function unstable_getCurrentPriorityLevel() {
198 + return currentPriorityLevel_DEPRECATED;
199 +}
200 +
201 +export function unstable_next<T>(callback: () => T): T {
202 + let priorityLevel;
203 + switch (currentPriorityLevel_DEPRECATED) {
204 + case ImmediatePriority:
205 + case UserBlockingPriority:
206 + case NormalPriority:
207 + // Shift down to normal priority
208 + priorityLevel = NormalPriority;
209 + break;
210 + default:
211 + // Anything lower than normal priority should remain at the current level.
212 + priorityLevel = currentPriorityLevel_DEPRECATED;
213 + break;
214 + }
215 +
216 + const previousPriorityLevel = currentPriorityLevel_DEPRECATED;
217 + currentPriorityLevel_DEPRECATED = priorityLevel;
218 + try {
219 + return callback();
220 + } finally {
221 + currentPriorityLevel_DEPRECATED = previousPriorityLevel;
222 + }
223 +}
224 +
225 +export function unstable_wrapCallback<T>(callback: () => T): () => T {
226 + const parentPriorityLevel = currentPriorityLevel_DEPRECATED;
227 + return () => {
228 + const previousPriorityLevel = currentPriorityLevel_DEPRECATED;
229 + currentPriorityLevel_DEPRECATED = parentPriorityLevel;
230 + try {
231 + return callback();
232 + } finally {
233 + currentPriorityLevel_DEPRECATED = previousPriorityLevel;
234 + }
235 + };
236 +}
237 +
238 +export function unstable_forceFrameRate() {}
239 +
240 +export function unstable_pauseExecution() {}
241 +
242 +export function unstable_continueExecution() {}
243 +
244 +export function unstable_getFirstCallbackNode() {
245 + return null;
246 +}
247 +
248 +// Currently no profiling build
249 +export const unstable_Profiling = null;
packages/scheduler/src/__tests__/SchedulerPostTask-test.js
+134 -98
@@ -17,18 +17,16 @@ let runtime;
17 let performance;
18 let cancelCallback;
19 let scheduleCallback;
20 +let ImmediatePriority;
21 let NormalPriority;
22 +let UserBlockingPriority;
23 +let LowPriority;
24 +let IdlePriority;
25
26 // The Scheduler postTask implementation uses a new postTask browser API to
23 -// schedule work on the main thread. Most of our tests treat this as an
24 -// implementation detail; however, the sequence and timing of browser
25 -// APIs are not precisely specified, and can vary across browsers.
26 -//
27 -// To prevent regressions, we need the ability to simulate specific edge cases
28 -// that we may encounter in various browsers.
29 -//
30 -// This test suite mocks all browser methods used in our implementation. It
31 -// assumes as little as possible about the order and timing of events.s
27 +// schedule work on the main thread. This test suite mocks all browser methods
28 +// used in our implementation. It assumes as little as possible about the order
29 +// and timing of events.
30 describe('SchedulerPostTask', () => {
31 beforeEach(() => {
32 jest.resetModules();
@@ -37,18 +35,17 @@ describe('SchedulerPostTask', () => {
35 jest.mock('scheduler', () =>
36 require.requireActual('scheduler/unstable_post_task'),
37 );
40 - jest.mock('scheduler/src/SchedulerHostConfig', () =>
41 - require.requireActual(
42 - 'scheduler/src/forks/SchedulerHostConfig.post-task.js',
43 - ),
44 - );
38
39 runtime = installMockBrowserRuntime();
40 performance = window.performance;
41 Scheduler = require('scheduler');
42 cancelCallback = Scheduler.unstable_cancelCallback;
43 scheduleCallback = Scheduler.unstable_scheduleCallback;
44 + ImmediatePriority = Scheduler.unstable_ImmediatePriority;
45 + UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
46 NormalPriority = Scheduler.unstable_NormalPriority;
47 + LowPriority = Scheduler.unstable_LowPriority;
48 + IdlePriority = Scheduler.unstable_IdlePriority;
49 });
50
51 afterEach(() => {
@@ -58,14 +55,14 @@ describe('SchedulerPostTask', () => {
55 });
56
57 function installMockBrowserRuntime() {
61 - let hasPendingTask = false;
62 - let timerIDCounter = 0;
58 + let taskQueue = new Map();
59 let eventLog = [];
60
61 // Mock window functions
62 const window = {};
63 global.window = window;
64
65 + let idCounter = 0;
66 let currentTime = 0;
67 window.performance = {
68 now() {
@@ -73,28 +70,33 @@ describe('SchedulerPostTask', () => {
70 },
71 };
72
76 - window.setTimeout = (cb, delay) => {
77 - const id = timerIDCounter++;
78 - log(`Set Timer`);
79 - // TODO
80 - return id;
81 - };
82 - window.clearTimeout = id => {
83 - // TODO
84 - };
85 -
73 // Mock browser scheduler.
74 const scheduler = {};
75 global.scheduler = scheduler;
76
90 - let nextTask;
91 - scheduler.postTask = function(callback) {
92 - if (hasPendingTask) {
93 - throw Error('Task already scheduled');
77 + scheduler.postTask = function(callback, {priority, signal}) {
78 + const id = idCounter++;
79 + log(
80 + `Post Task ${id} [${priority === undefined ? '<default>' : priority}]`,
81 + );
82 + const controller = signal._controller;
83 + return new Promise((resolve, reject) => {
84 + taskQueue.set(controller, {id, callback, resolve, reject});
85 + });
86 + };
87 +
88 + global.TaskController = class TaskController {
89 + constructor() {
90 + this.signal = {_controller: this};
91 + }
92 + abort() {
93 + const task = taskQueue.get(this);
94 + if (task !== undefined) {
95 + taskQueue.delete(this);
96 + const reject = task.reject;
97 + reject(new Error('Aborted'));
98 + }
99 }
95 - log('Post Task');
96 - hasPendingTask = true;
97 - nextTask = callback;
100 };
101
102 function ensureLogIsEmpty() {
@@ -105,22 +107,26 @@ describe('SchedulerPostTask', () => {
107 function advanceTime(ms) {
108 currentTime += ms;
109 }
108 - function fireNextTask() {
110 + function flushTasks() {
111 ensureLogIsEmpty();
110 - if (!hasPendingTask) {
111 - throw Error('No task was scheduled');
112 - }
113 - hasPendingTask = false;
114 -
115 - 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.
121 - const task = nextTask;
122 - nextTask = null;
123 - task();
117 + const prevTaskQueue = taskQueue;
118 + taskQueue = new Map();
119 + for (const [, {id, callback, resolve, reject}] of prevTaskQueue) {
120 + try {
121 + log(`Task ${id} Fired`);
122 + callback(false);
123 + resolve();
124 + } catch (error) {
125 + log(`Task ${id} errored [${error.message}]`);
126 + reject(error);
127 + continue;
128 + }
129 + }
130 }
131 function log(val) {
132 eventLog.push(val);
@@ -135,7 +141,7 @@ describe('SchedulerPostTask', () => {
141 }
142 return {
143 advanceTime,
138 - fireNextTask,
144 + flushTasks,
145 log,
146 isLogEmpty,
147 assertLog,
@@ -144,16 +150,16 @@ describe('SchedulerPostTask', () => {
150
151 it('task that finishes before deadline', () => {
152 scheduleCallback(NormalPriority, () => {
147 - runtime.log('Task');
153 + runtime.log('A');
154 });
149 - runtime.assertLog(['Post Task']);
150 - runtime.fireNextTask();
151 - runtime.assertLog(['Task Event', 'Task']);
155 + runtime.assertLog(['Post Task 0 [user-visible]']);
156 + runtime.flushTasks();
157 + runtime.assertLog(['Task 0 Fired', 'A']);
158 });
159
160 it('task with continuation', () => {
161 scheduleCallback(NormalPriority, () => {
156 - runtime.log('Task');
162 + runtime.log('A');
163 while (!Scheduler.unstable_shouldYield()) {
164 runtime.advanceTime(1);
165 }
@@ -162,13 +168,18 @@ describe('SchedulerPostTask', () => {
168 runtime.log('Continuation');
169 };
170 });
165 - runtime.assertLog(['Post Task']);
171 + runtime.assertLog(['Post Task 0 [user-visible]']);
172
167 - runtime.fireNextTask();
168 - runtime.assertLog(['Task Event', 'Task', 'Yield at 5ms', 'Post Task']);
173 + runtime.flushTasks();
174 + runtime.assertLog([
175 + 'Task 0 Fired',
176 + 'A',
177 + 'Yield at 5ms',
178 + 'Post Task 1 [user-visible]',
179 + ]);
180
170 - runtime.fireNextTask();
171 - runtime.assertLog(['Task Event', 'Continuation']);
181 + runtime.flushTasks();
182 + runtime.assertLog(['Task 1 Fired', 'Continuation']);
183 });
184
185 it('multiple tasks', () => {
@@ -178,55 +189,42 @@ describe('SchedulerPostTask', () => {
189 scheduleCallback(NormalPriority, () => {
190 runtime.log('B');
191 });
181 - runtime.assertLog(['Post Task']);
182 - runtime.fireNextTask();
183 - runtime.assertLog(['Task Event', 'A', 'B']);
184 - });
185 -
186 - it('multiple tasks with a yield in between', () => {
187 - scheduleCallback(NormalPriority, () => {
188 - runtime.log('A');
189 - runtime.advanceTime(4999);
190 - });
191 - scheduleCallback(NormalPriority, () => {
192 - runtime.log('B');
193 - });
194 - runtime.assertLog(['Post Task']);
195 - runtime.fireNextTask();
192 runtime.assertLog([
197 - 'Task Event',
198 - 'A',
199 - // Ran out of time. Post a continuation event.
200 - 'Post Task',
193 + 'Post Task 0 [user-visible]',
194 + 'Post Task 1 [user-visible]',
195 ]);
202 - runtime.fireNextTask();
203 - runtime.assertLog(['Task Event', 'B']);
196 + runtime.flushTasks();
197 + runtime.assertLog(['Task 0 Fired', 'A', 'Task 1 Fired', 'B']);
198 });
199
200 it('cancels tasks', () => {
201 const task = scheduleCallback(NormalPriority, () => {
208 - runtime.log('Task');
202 + runtime.log('A');
203 });
210 - runtime.assertLog(['Post Task']);
204 + runtime.assertLog(['Post Task 0 [user-visible]']);
205 cancelCallback(task);
206 + runtime.flushTasks();
207 runtime.assertLog([]);
208 });
209
215 - it('throws when a task errors then continues in a new event', () => {
210 + it('an error in one task does not affect execution of other tasks', () => {
211 scheduleCallback(NormalPriority, () => {
217 - runtime.log('Oops!');
212 throw Error('Oops!');
213 });
214 scheduleCallback(NormalPriority, () => {
215 runtime.log('Yay');
216 });
223 - runtime.assertLog(['Post Task']);
224 -
225 - expect(() => runtime.fireNextTask()).toThrow('Oops!');
226 - runtime.assertLog(['Task Event', 'Oops!', 'Post Task']);
227 -
228 - runtime.fireNextTask();
229 - runtime.assertLog(['Task Event', 'Yay']);
217 + runtime.assertLog([
218 + 'Post Task 0 [user-visible]',
219 + 'Post Task 1 [user-visible]',
220 + ]);
221 + runtime.flushTasks();
222 + runtime.assertLog([
223 + 'Task 0 Fired',
224 + 'Task 0 errored [Oops!]',
225 + 'Task 1 Fired',
226 + 'Yay',
227 + ]);
228 });
229
230 it('schedule new task after queue has emptied', () => {
@@ -234,16 +232,16 @@ describe('SchedulerPostTask', () => {
232 runtime.log('A');
233 });
234
237 - runtime.assertLog(['Post Task']);
238 - runtime.fireNextTask();
239 - runtime.assertLog(['Task Event', 'A']);
235 + runtime.assertLog(['Post Task 0 [user-visible]']);
236 + runtime.flushTasks();
237 + runtime.assertLog(['Task 0 Fired', 'A']);
238
239 scheduleCallback(NormalPriority, () => {
240 runtime.log('B');
241 });
244 - runtime.assertLog(['Post Task']);
245 - runtime.fireNextTask();
246 - runtime.assertLog(['Task Event', 'B']);
242 + runtime.assertLog(['Post Task 1 [user-visible]']);
243 + runtime.flushTasks();
244 + runtime.assertLog(['Task 1 Fired', 'B']);
245 });
246
247 it('schedule new task after a cancellation', () => {
@@ -251,17 +249,55 @@ describe('SchedulerPostTask', () => {
249 runtime.log('A');
250 });
251
254 - runtime.assertLog(['Post Task']);
252 + runtime.assertLog(['Post Task 0 [user-visible]']);
253 cancelCallback(handle);
254
257 - runtime.fireNextTask();
258 - runtime.assertLog(['Task Event']);
255 + runtime.flushTasks();
256 + runtime.assertLog([]);
257
258 scheduleCallback(NormalPriority, () => {
259 runtime.log('B');
260 });
263 - runtime.assertLog(['Post Task']);
264 - runtime.fireNextTask();
265 - runtime.assertLog(['Task Event', 'B']);
261 + runtime.assertLog(['Post Task 1 [user-visible]']);
262 + runtime.flushTasks();
263 + runtime.assertLog(['Task 1 Fired', 'B']);
264 + });
265 +
266 + it('schedules tasks at different priorities', () => {
267 + scheduleCallback(ImmediatePriority, () => {
268 + runtime.log('A');
269 + });
270 + scheduleCallback(UserBlockingPriority, () => {
271 + runtime.log('B');
272 + });
273 + scheduleCallback(NormalPriority, () => {
274 + runtime.log('C');
275 + });
276 + scheduleCallback(LowPriority, () => {
277 + runtime.log('D');
278 + });
279 + scheduleCallback(IdlePriority, () => {
280 + runtime.log('E');
281 + });
282 + runtime.assertLog([
283 + 'Post Task 0 [user-blocking]',
284 + 'Post Task 1 [user-blocking]',
285 + 'Post Task 2 [user-visible]',
286 + 'Post Task 3 [user-visible]',
287 + 'Post Task 4 [background]',
288 + ]);
289 + runtime.flushTasks();
290 + runtime.assertLog([
291 + 'Task 0 Fired',
292 + 'A',
293 + 'Task 1 Fired',
294 + 'B',
295 + 'Task 2 Fired',
296 + 'C',
297 + 'Task 3 Fired',
298 + 'D',
299 + 'Task 4 Fired',
300 + 'E',
301 + ]);
302 });
303 });
packages/scheduler/src/forks/SchedulerHostConfig.post-task.js deleted
-99
@@ -1,99 +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 -// Capture local references to native APIs, in case a polyfill overrides them.
9 -const perf = window.performance;
10 -const setTimeout = window.setTimeout;
11 -const clearTimeout = window.clearTimeout;
12 -
13 -function postTask(callback) {
14 - // Use experimental Chrome Scheduler postTask API.
15 - global.scheduler.postTask(callback);
16 -}
17 -
18 -function getNow() {
19 - return perf.now();
20 -}
21 -
22 -let isTaskLoopRunning = false;
23 -let scheduledHostCallback = null;
24 -let taskTimeoutID = -1;
25 -
26 -// Scheduler periodically yields in case there is other work on the main
27 -// thread, like user events. By default, it yields multiple times per frame.
28 -// It does not attempt to align with frame boundaries, since most tasks don't
29 -// need to be frame aligned; for those that do, use requestAnimationFrame.
30 -const yieldInterval = 5;
31 -let deadline = 0;
32 -
33 -// `isInputPending` is not available. Since we have no way of knowing if
34 -// there's pending input, always yield at the end of the frame.
35 -export function shouldYieldToHost() {
36 - return getNow() >= deadline;
37 -}
38 -
39 -export function requestPaint() {
40 - // Since we yield every frame regardless, `requestPaint` has no effect.
41 -}
42 -
43 -export function forceFrameRate(fps) {
44 - // No-op
45 -}
46 -
47 -function performWorkUntilDeadline() {
48 - if (scheduledHostCallback !== null) {
49 - const currentTime = getNow();
50 - // Yield after `yieldInterval` ms, regardless of where we are in the vsync
51 - // cycle. This means there's always time remaining at the beginning of
52 - // the message event.
53 - deadline = currentTime + yieldInterval;
54 - const hasTimeRemaining = true;
55 - try {
56 - const hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
57 - if (!hasMoreWork) {
58 - isTaskLoopRunning = false;
59 - scheduledHostCallback = null;
60 - } else {
61 - // If there's more work, schedule the next message event at the end
62 - // of the preceding one.
63 - postTask(performWorkUntilDeadline);
64 - }
65 - } catch (error) {
66 - // If a scheduler task throws, exit the current browser task so the
67 - // error can be observed.
68 - postTask(performWorkUntilDeadline);
69 - throw error;
70 - }
71 - } else {
72 - isTaskLoopRunning = false;
73 - }
74 -}
75 -
76 -export function requestHostCallback(callback) {
77 - scheduledHostCallback = callback;
78 - if (!isTaskLoopRunning) {
79 - isTaskLoopRunning = true;
80 - postTask(performWorkUntilDeadline);
81 - }
82 -}
83 -
84 -export function cancelHostCallback() {
85 - scheduledHostCallback = null;
86 -}
87 -
88 -export function requestHostTimeout(callback, ms) {
89 - taskTimeoutID = setTimeout(() => {
90 - callback(getNow());
91 - }, ms);
92 -}
93 -
94 -export function cancelHostTimeout() {
95 - clearTimeout(taskTimeoutID);
96 - taskTimeoutID = -1;
97 -}
98 -
99 -export const getCurrentTime = getNow;
packages/scheduler/unstable_post_task.js
+1 -1
@@ -7,4 +7,4 @@
7
8 'use strict';
9
10 -export * from './src/Scheduler';
10 +export * from './src/SchedulerPostTask';
scripts/rollup/forks.js
-2
@@ -209,8 +209,6 @@ const forks = Object.freeze({
209 entry === 'react-test-renderer'
210 ) {
211 return 'scheduler/src/forks/SchedulerHostConfig.mock';
212 - } else if (entry === 'scheduler/unstable_post_task') {
213 - return 'scheduler/src/forks/SchedulerHostConfig.post-task';
212 }
213 return 'scheduler/src/forks/SchedulerHostConfig.default';
214 },
scripts/rollup/validate/eslintrc.cjs.js
+2
@@ -30,6 +30,8 @@ module.exports = {
30 Int32Array: true,
31 ArrayBuffer: true,
32
33 + TaskController: true,
34 +
35 // Flight
36 Uint8Array: true,
37 Promise: true,
scripts/rollup/validate/eslintrc.cjs2015.js
+2
@@ -30,6 +30,8 @@ module.exports = {
30 Int32Array: true,
31 ArrayBuffer: true,
32
33 + TaskController: true,
34 +
35 // Flight
36 Uint8Array: true,
37 Promise: true,
scripts/rollup/validate/eslintrc.fb.js
+2
@@ -31,6 +31,8 @@ module.exports = {
31 Int32Array: true,
32 ArrayBuffer: true,
33
34 + TaskController: true,
35 +
36 // Flight
37 Uint8Array: true,
38 Promise: true,
scripts/rollup/validate/eslintrc.rn.js
+2
@@ -29,6 +29,8 @@ module.exports = {
29 SharedArrayBuffer: true,
30 Int32Array: true,
31 ArrayBuffer: true,
32 +
33 + TaskController: true,
34 },
35 parserOptions: {
36 ecmaVersion: 5,
scripts/rollup/validate/eslintrc.umd.js
+2
@@ -34,6 +34,8 @@ module.exports = {
34 Int32Array: true,
35 ArrayBuffer: true,
36
37 + TaskController: true,
38 +
39 // Flight
40 Uint8Array: true,
41 Promise: true,